首页 > 解决方案 > Angular9 可重复使用的多自动完成芯片输入为 FormArray / ERROR TypeError: control.registerOnChange is not a function

问题描述

健康)状况

表格组件- 带有行创建/编辑按钮的表格。单击这些按钮中的任何一个时,将打开带有输入字段的相同对话框组件。输入字段是一个可重复使用的多自动完成芯片组件。如果用户单击“编辑行”,则必须将给定值设置为输入字段作为 mat-chips 并保持可编辑(删除芯片或添加新的)。如果选择“创建行” - 空输入,用户可以添加新的。

问题

当我打开编辑对话框时,我确实将表中的值设置为输入字段。但有了它我得到一个错误:

错误类型错误:control.registerOnChange 不是函数

我无法理解的对话框组件和输入组件之间存在联系。我认为问题出在FormControlControlValueAccessor中。

代码

对话框.component.ts:

    this.fb.group({
                *here are some working FormControls*,
                managers: this.fb.array(this.someInterface.managers)

对话框.component.html:

                <managers-auto-chips-selector
                        formControlName="managers">
                </managers-auto-chips-selector>

input.component.html:

    <mat-form-field appearance="outline">
                <mat-label>Manager</mat-label>
                <mat-chip-list #chipList>
                    <mat-chip
                            *ngFor="let item of selectedList"
                            [selectable]="selectable"
                            [removable]="removable"
                            (removed)="remove(item)">
                        {{item.name}}
                        <mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
                    </mat-chip>
                    <input matInput
                           placeholder="Manager"
                           #managerInput
                           [matAutocomplete]="autocomplete"
                           [formControl]="managerControl"
                           [matChipInputFor]="chipList"
                           [matChipInputSeparatorKeyCodes]="separatorKeysCodes"
                           [matChipInputAddOnBlur]="addOnBlur"
                           (matChipInputTokenEnd)="add($event)">
                </mat-chip-list>

                <mat-autocomplete #autocomplete="matAutocomplete" (optionSelected)="selected($event)">
                    <mat-option *ngFor="let item of filteredList | async" [value]="item">
                        {{ item.name }}
                    </mat-option>
                </mat-autocomplete>
            </mat-form-field>

input.component.ts(添加):

    filteredList: Observable<Manager[]>;
    list: Manager[] = [];  <--- managers list filled from API
    selectedList: Manager[] = [];
    managerControl = new FormControl();
    constructor( @Optional() @Self() public ngControl: NgControl) {
    if (this.ngControl != null) {
         this.ngControl.valueAccessor = this;
       }
    }

    ngOnInit(): void {
        this.filteredList = this.managerControl.valueChanges.pipe(
            map((manager: string | null) => {
                return manager ? this._filter(manager) : 
              this.list.slice();
            })
        );

        this.getManagersList();
    }
    private _filter(value: any): any[] {

        return this.list.filter(manager => {
            if (typeof value === 'string') {
                return manager.fio.toLowerCase().includes(value.toLowerCase());
            } else {
                return manager.fio.toLowerCase().includes(value.fio.toLowerCase());
            }
        });
    }
    add(event: MatChipInputEvent): void {
        const input = event.input;
        const value = event.value;

        // Add our fruit
        if ((value || '').trim()) {

            const item = this.list.filter(manager => {
                return manager.fio.toLowerCase().includes(value.toLowerCase());
            });

            if (item.length === 1) {
                this.selectedList.push(item[0]);
            }
        }

        // Reset the input value
        if (input) {
            input.value = '';
        }

        this.managerControl.setValue(null);
    }
    selected(event: MatAutocompleteSelectedEvent): void {
        this.selectedList.push(event.option.value);
        this.managerInput.nativeElement.value = '';
        this.managerControl.setValue(null);
    }

    registerOnChange(fn: any): void {
    }

    registerOnTouched(fn: any): void {
    }

    writeValue(item: any): void {
       this.selectedList = item;
    }

我有几个建议下一步该去哪里,但其中任何一个都会让我远离我目前的结果。仍然:

1) 在 dialog.component.htmlformControlNameformArrayName. 如果我这样做,则不会出现错误,但我的值没有设置并且我有空白输入。
2) 在 input.component.tsmanagerControl = new FormControl()new FormArray([ ]). 然后我收到了一些(!)错误消息:

错误类型错误:control.registerOnChange 不是函数

3)要投入使用的东西registerOnChange。无法得到它。
4) 以某种方式将每个 FormControl 从 dialog.components.ts 推managers: this.fb.array(this.someInterface.managers)managerControl = new FormArray([]);
5) ???

任何意见或建议表示赞赏。
将根据需要提供任何更深入的解释、额外的代码或屏幕截图。
谢谢你。

标签: angularangular-reactive-formsangular2-formbuildercontrolvalueaccessor

解决方案


一些关于如何填充表单的代码会非常有用。

使用 FormArray 而不是 FormControl 作为managers属性。如果你的价值观没有设定,那么你做错了。

您可以在下面找到一个示例,说明如何做到这一点并根据您的用例进行调整:

ngOnInit() {
 this.myForm = this.fb.group({
      ...
      managers: this.fb.array([], Validators.required),
    });

  this.myService.getWhatever().subscribe(whatever => {
    if(whatever) {
      this.myForm.setValue({
       ...
      // set it to an empty array
      managers: [] 
     });

     // push the data that you get to the form array
     for (const manager of whatever.managers) {
        this.managers.push(new FormControl(manager));
      }
   }
 }
}

 // Getter for retrieving the tags form array control from the parent form group
  public get managers(): FormArray {
    return this.myForm.get('managers') as FormArray;
  }
<mat-chip-list #chipList formArrayName="managers">
  <mat-chip 
    *ngFor="let manager of managers.controls; index as i"
   >
     {{ manager.name }}
    <mat-icon matChipRemove *ngIf="removable">cancel</mat-icon>
  </mat-chip>
</mat-chip-list>

推荐阅读