Angular - Реактивная форма Validators.required на пользовательский элемент управления - PullRequest
0 голосов
/ 06 января 2020

У меня есть простая группа FormGroup, как это:

  const form = this.fb.group({
  type: ['', { validators: Validators.required, updateOn: 'change' }],

  doc_no: ['', Validators.required],
  currency: ['', Validators.required],
  exchange_rate: [1, Validators.required],
  ...

При работе с простым HTML вводом, подобным этому:

<input formControlName="exchange_rate" />

В этом случае, когда я очищаю входное значение, Angular обрабатывает Validators.required должным образом, и ввод получает ng-invalid класс, а также вся форма попадает в недопустимое состояние.

Однако, когда я перехожу к пользовательскому элементу управления (в основном это простой упакованный ввод с предопределенными преобразованиями и некоторые другие методы, такие как:

<app-input-number formControlName="exchange_rate" ></app-input-number>

Angular не устанавливает недопустимый класс ng при очистке значения. Однако, когда я изменяю значение или даже просто фокусирую ввод, Angular устанавливает атрибут ng-touch.

Я явно что-то упустил. Могу ли я получить подсказку?

input-number.component. html:

<input class="form-control" 
    type="text" 
    [value]="number | decimal2:'0.0-8'" 
    (change)="onChange($event)"
    (blur)="onBlur($event)" 
    [disabled]="disabled"
    [readonly]="readonly"
    [required]="required"
>

input -number.component.ts:

import { Component, Input, Output, forwardRef, EventEmitter, ViewEncapsulation } from '@angular/core';
import { NG_VALUE_ACCESSOR, NG_VALIDATORS, Validator, ControlValueAccessor, FormControl, ValidationErrors } from '@angular/forms';
import { Converter } from '@app/core/';

@Component({
    selector: 'app-input-number',
    templateUrl: './input-number.component.html',
    styleUrls: ['./input-number.component.scss'],

    encapsulation: ViewEncapsulation.None,

    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => InputNumberComponent),
            multi: true,
        },
        {
            provide: NG_VALIDATORS,
            useExisting: forwardRef(() => InputNumberComponent),
            multi: true,
        }
    ]
})
export class InputNumberComponent implements Validator, ControlValueAccessor {

    @Input()
    disabled = false;

    @Input()
    readonly = false;

    @Input()
    required = false;

    @Input()
    number?: number;

    @Output()
    numberChange = new EventEmitter<number>();

    constructor(
        private converts: Converter
    ) {

    }

    onChange(event?: any) {

        if (this.disabled) {
            return;
        }

        const _value = event.target.value;

        const _number = this.converts.toNumber(_value);

        if (!_number) {
            return;
        }

        this.processChange(_value);
    }

    private processChange(value: any) {

        if (!value || value === '') {
            this.number = null;
            this.numberChange.emit(null);
            this.onChanged(null);

            return;
        }

        let _number: number;

        try {
            _number = this.converts.toNumber(value);
        } catch (ex) {
            return;
        }

        this.number = _number;
        this.numberChange.emit(_number);
        this.onChanged(_number);
    }

    // ControlValueAccessor INTERFACE

    // the method set in registerOnChange, it is just
    // a placeholder for a method that takes one parameter,
    // we use it to emit changes back to the form
    private onChanged = (_: any) => { };
    private onTouched = () => { };

    onBlur(event?: any) {
        this.onTouched();
    }

    // this is the initial value set to the component
    writeValue(value: any): void {

        if (this.disabled) {
            return;
        }

        if (value !== this.number) {
            try {

                if (!isNaN(value)) {
                    this.number = (+value);
                    return;
                }

                this.number = this.converts.toNumber(value);
                // const _number = this.converts.toNumber(value);

                // this.number = this.converts.toString(_number);
            } catch (ex) {
                this.number = null;
                return;
            }
        }
    }

    // registers 'fn' that will be fired when changes are made
    // this is how we emit the changes back to the form
    registerOnChange(fn: any): void {
        this.onChanged = fn;
    }

    registerOnTouched(fn: any): void {
        this.onTouched = fn;
    }

    setDisabledState(isDisabled: boolean): void {
        this.disabled = isDisabled;
    }

    // VALIDATOR INTERFACE
    validate(control: FormControl): ValidationErrors {

        if (this.required && !this.number) {
            return {
                invalid: true
            };
        }

        return null;
    }

    registerOnValidatorChange?(fn: () => void): void {
        // console.log('registerOnValidatorChange', fn);
    }
}

Спасибо

...