I am currently trying to make an input that is actually a different input for each letter in Angular 9. The problem I seem to be having currently is with change detection. The two ways that I see to do this are in conflict with each other. changes.subscribe() from @ViewChildren() doesn't update when I use a custom trackBy in my *ngFor, but I need the trackBy to prevent all kinds of issues with changes in one box propagating in another. I could use the custom trackBy function, but it cannot access data from my Component. Here is my component:
@Component({
selector: 'app-multiple-fields',
templateUrl: './multiple-fields.component.html',
styleUrls: ['./multiple-fields.component.css'],
providers: [
{
provide: NG_VALUE_ACCESSOR,
multi: true,
useExisting: forwardRef(() => MultipleFieldsComponent),
},
],
})
export class MultipleFieldsComponent
implements OnInit, AfterViewInit, ControlValueAccessor {
@Input() numFields: number = 6;
@ViewChildren('input', { read: ElementRef }) inputs: QueryList<
ElementRef
>;
fields: string[];
onChange: any = () => {};
onTouch: any = () => {};
ngAfterViewInit() {
this.inputs.changes.subscribe((next: QueryList<ElementRef>) => {
let val = next.map((el) => el.nativeElement.value).join('');
this.onChange(val);
this.onTouch(val);
});
}
ngOnInit() {
this.fields = Array(this.numFields).fill('');
}
trackArray(index, item) {
return index;
}
writeValue(value: string) {
if (value.length == this.numFields) this.fields = value.split('');
}
registerOnChange(fn: any) {
this.onChange = fn;
}
registerOnTouched(fn: any) {
this.onTouch = fn;
}
}
Here is the template for the component:
<input
#input
*ngFor="let item of fields; let i = index; trackBy: trackArray"
[(ngModel)]="fields[i]"
/>
My hope is to be able to detect any changes in the fields[] array and then call onChange() and onTouch() with the joined value to be able to use [(ngModel)] on the component usage. Any help would be greatly appreciated. Thank you!