Угловой компонент, который реализует ControlValueAccessor, терпит неудачу для двухстороннего связывания - PullRequest
0 голосов
/ 08 июня 2018

Пользовательский компонент, обертывающий элемент управления выбора материала и реализующий ControlValueAccessor, не может выполнить двухстороннее связывание, но успешно связывает один из способов.

Component.ts

@Component({
    selector: 'page-size-select',
    templateUrl: 'page-size-select.component.html',
    inputs: ['pageSize'],
    providers: [{
        provide: NG_VALUE_ACCESSOR,
        useExisting: PageSizeSelectComponent,
        multi: true
    }]
})
export class PageSizeSelectComponent  implements ControlValueAccessor{

    pageSizes = [
        { id: 10, name: '10' },
        { id: 25, name: '25' },
        { id: 50, name: '50' },
        { id: -1, name: 'All' }
    ];

    innerPageSize: number;

    constructor() {}

    private changed = new Array<(value: number) => void>();
    private touched = new Array<() => void>();

    get pageSize(): number {
        return this.innerPageSize;
    }

    set pageSize(pageSize: number) {
        if (this.innerPageSize !== pageSize) {
            this.innerPageSize = pageSize;
            this.changed.forEach(f => f(pageSize));
        }
    }

    touch() {
        this.touched.forEach(f => f());
    }

    writeValue(pageSize: number) {
        this.innerPageSize = pageSize;
    }

    registerOnChange(fn: (value: number) => void) {
        this.changed.push(fn);
    }


    registerOnTouched(fn: () => void) {
        this.touched.push(fn);
    }
}

Шаблон компонента

<mat-form-field>
    <mat-select placeholder="Page Size..." [(ngModel)]="pageSize" name="pageSize" role="menu">
        <mat-option *ngFor="let opt of pageSizes" [value]="opt.id" role="menuitem">
            {{opt.name}}
        </mat-option>
    </mat-select>
</mat-form-field>

Успешное использование:

  <page-size-select [pageSize]="pagination.pageSize" ></page-size-select>

Сбой использования:

  <page-size-select ([pageSize])="pagination.pageSize" ></page-size-select>

Когда одностороннее связывание успешно, оно успешно читаетpagination.pageSize значение в базовый элемент управления select и устанавливает выбранную опцию в это значение.Однако целью является двухсторонняя привязка, но при использовании этого синтаксиса привязка не читает и не записывает в pagination.pageSize.

1 Ответ

0 голосов
/ 08 июня 2018

Во-первых, у вас есть синтаксическая ошибка:

([pageSize])="pagination.pageSize"

неверно.Квадратные скобки идут снаружи, а не круглые:

[(pageSize)]="pagination.pageSize"

Кроме того, я не уверен, что ваша реализация ControlValueAccessor также верна.Цель интерфейса - заставить компонент работать с угловыми элементами управления.Вы используете FormControl, не осознавая этого, когда используете ngModel.Таким образом, writeValue должен вызывать зарегистрированную функцию изменения - таким образом, изменения в компоненте отражаются обратно к зарегистрированному FormControl, который должен иметь место для связывания записи с ngModel.Вы не можете иметь более одной FormControl, поэтому вам не нужно «управлять несколькими подписками».Точно так же вам нужно вызывать вашу зарегистрированную сенсорную функцию - обычно, когда компонент размыт.Я сделал что-то очень похожее на то, что вы делаете, и моя реализация ControlValueAccessor выглядит следующим образом (в значительной степени вырезка и вставка примера Angular):

writeValue(value: any) {
    if (value !== this._value) {
        this._value = value; // this is my internal value model
        this._onChange(value);
        this.change.emit(this._value); // I fire change events
    }
}

_onChange: (value: any) => void = () => {};
registerOnChange(fn: (value: any) => void) {
    this._onChange = fn;
}

_onTouched: () => any = () => {};
registerOnTouched(fn: any) {
    this._onTouched = fn;
}

// if you implement disabled functionality you need this function
setDisabledState(value: boolean): void {
    this._disabled = value; // this is my internal disabled model
}

// and my component's (blur) handler
onBlur() {
    this._onTouched();
    this.blur.emit(this._value); // I fire blur events
}
...