Angular 7 Custom mat-checkbox не обновляет действительность формы с ControlValueAccessor

Я создал настраиваемый компонент mat-checkbox и реализовал интерфейс ControlValueAccessor для связи с переданным formControl или ngModel.

Флажок обновляет назначенный formControl при изменении, однако моя проблема в том, что он, похоже, не регистрирует функции валидатора, управляемые шаблоном или реактивным способом.

Это моя составляющая:

import {Component, Input, OnInit, Self, ViewEncapsulation} from '@angular/core';
import {ControlValueAccessor, NgControl} from '@angular/forms';

@Component({
    selector: 'app-checkbox',
    templateUrl: './checkbox.component.html',
    styleUrls: ['./checkbox.component.scss'],
    encapsulation: ViewEncapsulation.None
})
export class CheckboxComponent implements OnInit, ControlValueAccessor {

    @Input() id: string;
    @Input('aria-label') ariaLabel: string;
    @Input() label: string;

    private isChecked = false;
    private isDisabled = false;

    onChange = (isChecked: boolean) => {};
    onTouched = () => {};

    constructor(@Self() public controlDir: NgControl) {
        // providing component as ControlValueAccessor
        controlDir.valueAccessor = this;
    }

    ngOnInit() {
        // Initializing assigned FormControl and providing it's Validator
        const control = this.controlDir.control;
        control.setValidators(control.validator);
        control.updateValueAndValidity();
    }

    /**
     * ControlValueAccessor interface (https://angular.io/api/forms/ControlValueAccessor)
     * Registers a callback function that is called when the control's value changes in the UI.
     * @param fn callback function
     */
    registerOnChange(fn: (value: boolean) => void): void {
        this.onChange = fn;
    }

    /**
     * Input change callback that passes the changed string to this.onChange method
     */
    valueChanged(value: boolean) {
        this.onChange(value);
    }

    /**
     * ControlValueAccessor interface
     * Registers a callback function is called by the forms API on initialization to update the form model on blur.
     * @param fn callback function
     */
    registerOnTouched(fn: () => void): void {
        this.onTouched = fn;
    }

    /**
     * (optional) - ControlValueAccessor interface
     * Function that is called by the forms API when the control status changes to or from 'DISABLED'. Depending on the status, it enables or disables the appropriate DOM element.
     * @param isDisabled control state
     */
    setDisabledState(isDisabled: boolean): void {
        this.isDisabled = isDisabled;
    }

    /**
     * ControlValueAccessor interface
     * Writes a new value to the element.
     * @param value new control value
     */
    writeValue(value: boolean): void {
        if (value) {
            this.isChecked = value;
        }
    }
}

И вот мой шаблон:

<mat-checkbox color="primary" [checked]="isChecked" [disabled]="isDisabled" [id]="id" [attr.aria-label]="ariaLabel" (change)="valueChanged($event.checked)" (blur)="onTouched()">
    {{label}}
</mat-checkbox>

Вот как я называю свой компонент основанным на шаблонах способом:

<form class="row border-bottom" #checkboxForm2="ngForm">
    <div class="col-12 col-md-4 my-1">
        checkboxForm2.value: <br>
        {{checkboxForm2.value | json}} <br>
        checkboxForm2.valid: {{checkboxForm2.valid}}
    </div>
    <div class="col-12 col-md-8 my-3">
        <app-checkbox [ngModel]="false" name="checkbox3" label="REQUIRED" required></app-checkbox>
    </div>
</form>

checkboxForm2.valid всегда остается верным. Как заставить работать требуемый валидатор?


person Boldizsar    schedule 15.05.2019    source источник
comment
Код выглядит нормально, можете ли вы попробовать console.log(control.validator) и убедиться, что там что-то требуется?   -  person sabithpocker    schedule 15.05.2019
comment
Почему onChange() ничего не делает в вашем пользовательском элементе управления?   -  person sabithpocker    schedule 15.05.2019
comment
Также в вашем случае вам, возможно, даже придется пойти на requiredTrue валидатор, не уверен, работает ли required, когда у вас есть значение как false   -  person sabithpocker    schedule 15.05.2019
comment
Спасибо, sabithpocker, валидатор requiredTrue работал, когда я установил его в formControl. Есть ли еще директива, которую я могу установить с помощью ngModel?   -  person Boldizsar    schedule 15.05.2019
comment
onChange() изначально был установлен в событии изменения моего флажка. Я просто поигрался с функцией valueChanged.   -  person Boldizsar    schedule 15.05.2019
comment
Вы можете найти required валидатор в control.validator и заменить его на requiredTrue в своем пользовательском компоненте. Убедитесь, что вы добавили достаточно комментариев, чтобы избежать путаницы. Также проверьте, как обстоят дела с материалами в их коде для mat-checkbox в github.   -  person sabithpocker    schedule 16.05.2019


Ответы (1)


Мне удалось заставить его работать, вызвав функцию, проверяющую, прикреплен ли элемент управления Validators.required, и установив Validators.requiredTrue в writeValue метод интерфейса CVA компонента. Вот мой код:

export class CheckboxComponent implements DoCheck, ControlValueAccessor {

...

    writeValue(value: boolean): void {
        this._isChecked = !!value;
        this._autoSelected = !!value;

        const control = this._controlDir.control;
        // Adding Validators.requiredTrue if Validators.required is set on control in order to make it work with template-driven required directive
        if (this.hasRequiredValidator(control)) {
            control.setValidators([control.validator, Validators.requiredTrue]);
        } else {
            control.setValidators(control.validator);
        }
    }

...

    /**
     * Returns whether checkbox is required
     * @param abstractControl control assigned to component
     */
    hasRequiredValidator(abstractControl: AbstractControl): boolean {
        if (abstractControl.validator) {
            const validator = abstractControl.validator({} as AbstractControl);
            if (validator && validator.required) {
                return true;
            }
        }
        return false;
    }
person Boldizsar    schedule 16.05.2019