Многоразовый ввод с несколькими автозаполнениями в Angular9 как FormArray / ERROR TypeError: control.registerOnChange не является функцией - PullRequest
0 голосов
/ 26 мая 2020

Условие

Компонент таблицы - таблица с кнопкой создания / редактирования строки. При нажатии на любую из этих кнопок открывается тот же компонент диалога с полем ввода. Поле ввода - многоразовые микросхемы с автозаполнением компонент . Если пользователь нажимает «редактировать строку», указанные значения должны быть установлены в поле ввода как матовые фишки и оставаться доступными для редактирования (удалить фишку (ы) или добавить новую). Если выбрано «создать строку» - пустой ввод, пользователь может добавлять новые.

Проблема

Когда я открываю диалог редактирования, у меня есть значения из таблицы, установленные в поле ввода. Но с ним я получаю сообщение об ошибке:

ERROR TypeError: control.registerOnChange не является функцией

Существует связь между диалоговым компонентом и компонентом ввода, о чем я не могу понять. Я предполагаю, что проблема где-то в FormControl и ControlValueAccessor .

Код

dialog.component.ts:

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

dialog.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) В диалоговом компоненте. html formControlName до formArrayName. Если я это сделаю, ошибка не появится, но мои значения не установлены, и у меня пустой ввод.
2) В input.component.ts от managerControl = new FormControl() до new FormArray([ ]). Затем у меня появилось несколько (!) Сообщений об ошибках:

ERROR TypeError: control.registerOnChange не является функцией

3) Что-то, что нужно поместить в функцию registerOnChange. Не могу понять.
4) Каким-то образом pu sh каждый FormControl от dialog.components.ts managers: this.fb.array(this.someInterface.managers) до managerControl = new FormArray([]);
5) ???

Любые комментарии или советы приветствуется.
Более подробные объяснения, дополнительный код или скриншоты будут предоставлены по запросу.
Спасибо.

1 Ответ

0 голосов
/ 27 мая 2020

Некоторый код о том, как вы заполняете форму, был бы очень полезен.

Используйте 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>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...