import { ChangeDetectionStrategy, ChangeDetectorRef, Component, DestroyRef, EventEmitter, inject, Input, OnInit, Output } from '@angular/core'; import {AsyncValidatorFn, FormArray, FormControl, FormGroup, ReactiveFormsModule, ValidatorFn} from "@angular/forms"; import {TranslocoDirective} from "@jsverse/transloco"; import {takeUntilDestroyed} from "@angular/core/rxjs-interop"; import {debounceTime, distinctUntilChanged, tap} from "rxjs/operators"; @Component({ selector: 'app-edit-list', imports: [ReactiveFormsModule, TranslocoDirective], templateUrl: './edit-list.component.html', styleUrl: './edit-list.component.scss', changeDetection: ChangeDetectionStrategy.OnPush }) export class EditListComponent implements OnInit { private readonly cdRef = inject(ChangeDetectorRef); private readonly destroyRef = inject(DestroyRef); @Input({required: true}) items: Array = []; @Input({required: true}) label = ''; @Input() validators: ValidatorFn[] = [] @Input() asyncValidators: AsyncValidatorFn[] = []; // TODO: Make this more dynamic based on which validator failed @Input() errorMessage: string | null = null; @Output() updateItems = new EventEmitter>(); form: FormGroup = new FormGroup({items: new FormArray([])}); get ItemsArray(): FormArray { return this.form.get('items') as FormArray; } ngOnInit() { this.items.forEach(item => this.addItem(item)); this.form.valueChanges.pipe( debounceTime(100), distinctUntilChanged(), tap(data => this.emit()), takeUntilDestroyed(this.destroyRef)) .subscribe(); this.cdRef.markForCheck(); } createItemControl(value: string = ''): FormControl { return new FormControl(value, this.validators, this.asyncValidators); } add() { this.ItemsArray.push(this.createItemControl()); this.emit(); this.cdRef.markForCheck(); } addItem(value: string) { this.ItemsArray.push(this.createItemControl(value)); } remove(index: number) { // If it's the last item, just clear its value if (this.ItemsArray.length === 1) { this.ItemsArray.at(0).setValue(''); this.emit(); this.cdRef.markForCheck(); return; } this.ItemsArray.removeAt(index); this.emit(); this.cdRef.markForCheck(); } // Emit non-empty item values emit() { const nonEmptyItems = this.ItemsArray.controls .map(control => control.value) .filter(value => value !== null && value.trim() !== ''); this.updateItems.emit(nonEmptyItems); } }