File

src/app/core/common-components/icon-input/icon-input.component.ts

Description

A MatFormField to let the user define a FontAwesome icon (showing some additional explanation in the UI).

Implements

OnInit OnChanges

Metadata

Index

Properties
Inputs
Outputs

Inputs

control
Type : FormControl<string | null>
icon
Type : string

Outputs

iconChange
Type : EventEmitter

Properties

iconControl
Type : FormControl<string | null>
import {
  Component,
  EventEmitter,
  Input,
  OnChanges,
  OnInit,
  Output,
  SimpleChanges,
  inject,
} from "@angular/core";
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({
  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, OnChanges {
  private readonly iconLibrary = inject(FaIconLibrary);

  @Input() icon: string;
  @Input() control?: FormControl<string | null>;
  @Output() iconChange = new EventEmitter<string>();

  iconControl: FormControl<string | null>;

  ngOnInit(): void {
    const iconValidator = this.createIconValidator();
    if (this.control) {
      this.control.addValidators(iconValidator);
      this.control.updateValueAndValidity({ emitEvent: false });
      this.iconControl = this.control;
    } else {
      this.iconControl = new FormControl<string | null>(this.icon || "", {
        validators: [iconValidator],
      });
    }

    this.iconControl.valueChanges.subscribe((value) =>
      this.iconChange.emit(value || ""),
    );
  }

  ngOnChanges(changes: SimpleChanges): void {
    if (changes["icon"] && this.iconControl && !this.control) {
      this.iconControl.setValue(this.icon || "", { emitEvent: false });
    }
  }

  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>
    &nbsp;
    <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
Component
Html element with directive

results matching ""

    No results matching ""