src/app/core/basic-datatypes/string/edit-url/edit-url.component.ts

Extends

CustomFormControlDirective<string>

Implements

EditComponent OnInit

Metadata

Relationships

Used by

No results matching.

Index

Properties
Methods
Inputs
Outputs

Inputs

formFieldConfig
Type : FormFieldConfig
aria-describedby
Type : string
disabled
Type : boolean
ngControl
Type : any
Default value : inject(NgControl, { optional: true, self: true })
placeholder
Type : string
required
Type : boolean
value
Type : T

Outputs

valueChange
Type : EventEmitter

Methods

openLinkIfDisabled
openLinkIfDisabled(event?: Event)

Opens the URL in a new tab if the input field is disabled.

Parameters :
Name Type Optional
event Event Yes
Returns : void
blur
blur()
Returns : void
focus
focus()
Returns : void
onContainerClick
onContainerClick(event: MouseEvent)
Parameters :
Name Type Optional
event MouseEvent No
Returns : void
registerOnChange
registerOnChange(fn: any)
Parameters :
Name Type Optional
fn any No
Returns : void
registerOnTouched
registerOnTouched(fn: any)
Parameters :
Name Type Optional
fn any No
Returns : void
setDescribedByIds
setDescribedByIds(ids: string[])
Parameters :
Name Type Optional
ids string[] No
Returns : void
setDisabledState
setDisabledState(isDisabled: boolean)
Parameters :
Name Type Optional
isDisabled boolean No
Returns : void
writeValue
writeValue(value: T, notifyFormControl: unknown)

Implementation for Angular ControlValueAccessor interface that links the form control value to the component value

Parameters :
Name Type Optional Default value Description
value T No

The new value to set

notifyFormControl unknown No false

Whether to notify the FormControl of this change (for internal updates)

Returns : void

Properties

controlType
Type : string
Default value : "custom-control"
elementRef
Type : unknown
Default value : inject<ElementRef<HTMLElement>>(ElementRef)
Readonly enabled
Type : Signal<boolean>
Default value : computed(() => !this._disabled())

Whether the control is currently enabled, as a signal (tracks disabled).

errorStateMatcher
Type : unknown
Default value : inject(ErrorStateMatcher)
id
Type : unknown
Default value : `custom-form-control-${CustomFormControlDirective.nextId++}`
Static nextId
Type : number
Default value : 0
onChange
Type : unknown
Default value : () => {...}
onTouched
Type : unknown
Default value : () => {...}
parentForm
Type : unknown
Default value : inject(NgForm, { optional: true })
parentFormGroup
Type : unknown
Default value : inject(FormGroupDirective, { optional: true })
stateChanges
Type : unknown
Default value : new Subject<void>()
Readonly valueSignal
Type : Signal<T>
Default value : computed(() => this._value())

The current value of the control as a signal. Authoritative in both modes: it reflects the bound FormControl (synced in ngDoCheck) as well as [(value)] / writeValue updates.

import { CustomFormControlDirective } from "#src/app/core/common-components/basic-autocomplete/custom-form-control.directive";
import {
  ChangeDetectionStrategy,
  Component,
  input,
  OnInit,
} from "@angular/core";
import { FormsModule, ReactiveFormsModule } from "@angular/forms";
import { MatFormFieldControl } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatTooltipModule } from "@angular/material/tooltip";
import { FormFieldConfig } from "../../../common-components/entity-form/FormConfig";
import { DynamicComponent } from "../../../config/dynamic-components/dynamic-component.decorator";
import { EditComponent } from "../../../entity/entity-field-edit/dynamic-edit/edit-component.interface";

@DynamicComponent("EditUrl")
@Component({
  selector: "app-edit-url",
  templateUrl: "./edit-url.component.html",
  styleUrls: ["./edit-url.component.scss"],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [MatInputModule, FormsModule, ReactiveFormsModule, MatTooltipModule],
  providers: [{ provide: MatFormFieldControl, useExisting: EditUrlComponent }],
})
export class EditUrlComponent
  extends CustomFormControlDirective<string>
  implements EditComponent, OnInit
{
  formFieldConfig = input<FormFieldConfig>();

  ngOnInit() {
    this.formControl.valueChanges.subscribe((value) =>
      this.processUrlInput(value),
    );
  }

  /**
   * Ensures the URL starts with 'http://' or 'https://' while preventing duplication.
   */
  private processUrlInput(value: string): void {
    if (!value) return;

    let newValue = value.trim();

    if (newValue.startsWith("http://") || newValue.startsWith("https://")) {
      // if newValue has valid prefix, don't modify
    } else if (newValue.includes("://")) {
      // replace the prefix 'https://'
      newValue = "https://" + newValue.substring(newValue.indexOf("://") + 3);
    } else if ("https://".startsWith(newValue) && newValue.length > 1) {
      // delete the whole prefix if the user is deleting the prefix
      newValue = "";
    } else {
      newValue = "https://" + newValue;
    }

    if (this.formControl.value === newValue) {
      // nothing changed, don't update the form control
      return;
    }

    this.formControl.setValue(newValue, { emitEvent: false });

    const urlPattern =
      /^(https?:\/\/)?([\w.-]+)\.([a-z]{2,6}\.?)(\/[\w.-]*)*\/?$/i;
    this.formControl.setErrors(
      urlPattern.test(newValue) ? null : { invalid: true },
    );
  }

  /**
   * Opens the URL in a new tab if the input field is disabled.
   */
  openLinkIfDisabled(event?: Event) {
    if (this.formControl.disabled && this.formControl.value) {
      // Prevent any default behavior and stop propagation
      event?.preventDefault();
      event?.stopPropagation();
      window.open(this.formControl.value, "_blank");
    }
  }
}
<div
  (click)="openLinkIfDisabled()"
  [class.clickable]="!enabled() && valueSignal()"
>
  <input [formControl]="formControl" matInput type="url" />
</div>

./edit-url.component.scss

/* Keep the input disabled but visible */
input[disabled] {
  pointer-events: none;
}

.clickable {
  cursor: pointer;
  text-decoration: underline;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""