File
Description
A MatFormField to let the user define a FontAwesome icon
(showing some additional explanation in the UI).
Implements
Index
Properties
|
|
|
Inputs
|
|
|
Outputs
|
|
|
|
control
|
Type : FormControl<string | null>
|
|
|
|
iconControl
|
Type : FormControl<string | null>
|
|
|
import {
ChangeDetectionStrategy,
Component,
DestroyRef,
effect,
inject,
input,
OnInit,
output,
} from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
import {
AbstractControl,
FormControl,
ReactiveFormsModule,
ValidationErrors,
ValidatorFn,
} from "@angular/forms";
import { MatFormFieldModule } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatTooltipModule } from "@angular/material/tooltip";
import {
FaIconComponent,
FaIconLibrary,
} from "@fortawesome/angular-fontawesome";
import { resolveIconDefinition } from "../fa-dynamic-icon/fa-icon-utils";
/**
* A MatFormField to let the user define a FontAwesome icon
* (showing some additional explanation in the UI).
*/
@Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: "app-icon-input",
standalone: true,
imports: [
ReactiveFormsModule,
MatFormFieldModule,
MatInputModule,
MatTooltipModule,
FaIconComponent,
],
templateUrl: "./icon-input.component.html",
styleUrl: "./icon-input.component.scss",
})
export class IconComponent implements OnInit {
private readonly iconLibrary = inject(FaIconLibrary);
private readonly destroyRef = inject(DestroyRef);
icon = input<string>();
control = input<FormControl<string | null>>();
iconChange = output<string>();
iconControl: FormControl<string | null>;
constructor() {
effect(() => {
const iconValue = this.icon();
const externalControl = this.control();
if (this.iconControl && !externalControl) {
// Imperative FormControl sync is a side effect; keep this in effect (not computed).
this.iconControl.setValue(iconValue || "", { emitEvent: false });
}
});
}
ngOnInit(): void {
const iconValidator = this.createIconValidator();
const ctrl = this.control();
if (ctrl) {
ctrl.addValidators(iconValidator);
ctrl.updateValueAndValidity({ emitEvent: false });
this.iconControl = ctrl;
} else {
this.iconControl = new FormControl<string | null>(this.icon() || "", {
validators: [iconValidator],
});
}
this.iconControl.valueChanges
.pipe(takeUntilDestroyed(this.destroyRef))
.subscribe((value) => this.iconChange.emit(value || ""));
}
private createIconValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
const value =
typeof control.value === "string" ? control.value.trim() : "";
if (!value) {
return null;
}
const definition = resolveIconDefinition(value, this.iconLibrary);
return definition ? null : { invalidIcon: true };
};
}
}
<mat-form-field class="full-width" floatLabel="always">
<mat-label>
<span i18n>Icon</span>
<fa-icon
icon="question-circle"
matTooltip="Enter the ID of an icon here, which will be displayed as a visual icon then."
i18n-matTooltip
></fa-icon>
</mat-label>
<input matInput [formControl]="iconControl" placeholder="Enter icon name" />
@if (iconControl?.hasError("invalidIcon")) {
<mat-error i18n
>Unknown icon name. Use a valid Font Awesome icon.</mat-error
>
}
<mat-hint align="start" i18n>
Find available icon names at
<a
href="https://fontawesome.com/v6/search?o=r&m=free"
target="_blank"
rel="noopener"
>
fontawesome.com
</a>
</mat-hint>
</mat-form-field>
Legend
Html element with directive