src/app/features/todos/recurring-interval/edit-recurring-interval/edit-recurring-interval.component.ts

Description

Form field to edit a time interval for repetitions.

The schema or config accepts pre-defined options for the dropdown as additional.

Extends

CustomFormControlDirective<any>

Implements

OnInit EditComponent

Example

Metadata

Relationships

Used by

No results matching.

Depends on

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

openCustomIntervalSelection
openCustomIntervalSelection(event: MatOptionSelectionChange<TimeInterval>)
Parameters :
Name Type Optional
event MatOptionSelectionChange<TimeInterval> No
Returns : void
resetSelection
resetSelection()
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

compareOptionFun
Type : unknown
Default value : () => {...}
predefinedIntervals
Type : unknown
Default value : linkedSignal< { label: string; interval: TimeInterval }[] >( () => this.formFieldConfig()?.additional ?? [ { label: $localize`:default interval select option:weekly`, interval: { amount: 1, unit: "week" }, }, { label: $localize`:default interval select option:monthly`, interval: { amount: 1, unit: "month" }, }, ], )
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 { FormFieldConfig } from "#src/app/core/common-components/entity-form/FormConfig";
import {
  ChangeDetectionStrategy,
  Component,
  inject,
  input,
  linkedSignal,
  OnInit,
} from "@angular/core";
import { ReactiveFormsModule } from "@angular/forms";
import { MatOptionSelectionChange } from "@angular/material/core";
import { MatDialog } from "@angular/material/dialog";
import { MatFormFieldControl } from "@angular/material/form-field";
import { MatInputModule } from "@angular/material/input";
import { MatSelectModule } from "@angular/material/select";
import { FontAwesomeModule } from "@fortawesome/angular-fontawesome";
import { CustomFormControlDirective } from "../../../../core/common-components/basic-autocomplete/custom-form-control.directive";
import { DynamicComponent } from "../../../../core/config/dynamic-components/dynamic-component.decorator";
import { EditComponent } from "../../../../core/entity/entity-field-edit/dynamic-edit/edit-component.interface";
import { CustomIntervalComponent } from "../custom-interval/custom-interval.component";
import { generateLabelFromInterval, TimeInterval } from "../time-interval";

/**
 * Form field to edit a time interval for repetitions.
 *
 * The schema or config accepts pre-defined options for the dropdown as `additional`.
 */
@DynamicComponent("EditRecurringInterval")
@Component({
  selector: "app-edit-recurring-interval",
  templateUrl: "./edit-recurring-interval.component.html",
  styleUrls: ["./edit-recurring-interval.component.scss"],
  changeDetection: ChangeDetectionStrategy.OnPush,
  imports: [
    MatInputModule,
    ReactiveFormsModule,
    FontAwesomeModule,
    MatSelectModule,
  ],
  providers: [
    {
      provide: MatFormFieldControl,
      useExisting: EditRecurringIntervalComponent,
    },
  ],
})
export class EditRecurringIntervalComponent
  extends CustomFormControlDirective<any>
  implements OnInit, EditComponent
{
  private matDialog = inject(MatDialog);

  formFieldConfig = input<FormFieldConfig>();

  predefinedIntervals = linkedSignal<
    { label: string; interval: TimeInterval }[]
  >(
    () =>
      this.formFieldConfig()?.additional ?? [
        {
          label: $localize`:default interval select option:weekly`,
          interval: { amount: 1, unit: "week" },
        },
        {
          label: $localize`:default interval select option:monthly`,
          interval: { amount: 1, unit: "month" },
        },
      ],
  );

  compareOptionFun = (a: TimeInterval, b: TimeInterval) =>
    JSON.stringify(a) === JSON.stringify(b);

  ngOnInit(): void {
    // re-create active custom interval if necessary
    this.addCustomInterval(this.formControl.value);
  }

  resetSelection() {
    this.formControl.setValue(undefined);
  }

  openCustomIntervalSelection(event: MatOptionSelectionChange<TimeInterval>) {
    if (!event.isUserInput) {
      return;
    }

    const previousSelectedInterval = this.formControl.value;
    this.matDialog
      .open(CustomIntervalComponent)
      .afterClosed()
      .subscribe((result: TimeInterval) => {
        if (result === undefined) {
          // keep unchanged, i.e. revert to previous selection
          this.formControl.setValue(previousSelectedInterval);
        } else {
          this.addCustomInterval(result);
          this.formControl.setValue(result);
          this.formControl.markAsDirty();
        }
      });
  }

  private addCustomInterval(interval: TimeInterval) {
    if (!interval) {
      return;
    }

    const selectedOptionValue = this.predefinedIntervals().find((o) =>
      this.compareOptionFun(interval, o.interval),
    )?.interval;
    if (!selectedOptionValue) {
      this.predefinedIntervals.update((prev) => [
        ...prev,
        { label: generateLabelFromInterval(interval), interval },
      ]);
    }
  }
}
<mat-select [formControl]="formControl" [compareWith]="compareOptionFun">
  @for (o of predefinedIntervals(); track o.label) {
    <mat-option [value]="o.interval">
      {{ o.label }}
    </mat-option>
  }

  <mat-option
    (onSelectionChange)="openCustomIntervalSelection($event)"
    class="special-option"
  >
    <fa-icon icon="tools" [fixedWidth]="true"></fa-icon>
    <span i18n="recurring interval option">define other interval</span>
  </mat-option>

  <mat-option
    [value]="undefined"
    (onSelectionChange)="resetSelection()"
    class="special-option"
  >
    <fa-icon icon="xmark" [fixedWidth]="true"></fa-icon>
    <span i18n="recurring interval option">does not repeat</span>
  </mat-option>
</mat-select>

./edit-recurring-interval.component.scss

.special-option {
  font-style: italic;
}

.suffix-action {
  display: inline;
}

.tooltip-suffix {
  margin: 4px;
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""