ControlValueAccessor с проверкой ошибок в угловом материале

Я пытаюсь применить стиль проверки ошибок с помощью ControlValueAccessor в настраиваемом текстовом поле ввода материала. С тех пор, как был применен этот пользовательский компонент, все статусы проверки красной границы с formControlName / FormBuilders не отображаются для required, minlength и т. Д. Он работал изначально (из коробки) с текстовым полем Angular Material, пока не был применен пользовательский элемент управления.

Цель состоит в том, чтобы настраиваемое текстовое поле работало с проверкой формы. Это естественно проявилось с текстовым полем matInput.

Обновление: опубликованный ответ; однако не уверен, что это самый эффективный вариант, пытаясь заставить Saloo работать также (если кто-то может опубликовать stackbliz, было бы здорово), открыт для любых более эффективных вариантов

введите описание изображения здесь

Машинопись:

import { Component, OnInit, Input, ViewChild, EventEmitter, Output, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-input-textbox',
  templateUrl: './input-textbox.component.html',
  styleUrls: ['./input-textbox.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputTextboxComponent),
      multi: true
    }
  ]
})

export class InputTextboxComponent implements OnInit, ControlValueAccessor {
  @Input() MaxLength: string;
  @Input() Value: string;
  @Input() type: string;
  @Input() Label: string;
  @Input() PlaceHolder: string;
  @Output() saveValue = new EventEmitter();
  @Output() onStateChange = new EventEmitter();

  disabled: boolean;

  constructor() { }

  ngOnInit() {
  }

  saveValueAction(e) {
    this.saveValue.emit(e.target.value);
  }

  onChange(e) {
    this.Value = e;
  }

  onTouched() {
    this.onStateChange.emit();
  }

  writeValue(value: any) {
    this.Value = value ? value : '';
  }

  registerOnChange(fn: any) { this.onChange = fn; }

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

  setDisabledState(isDisabled) { this.disabled = isDisabled; }
}

HTML:

<div class="input-wrap">
    <mat-form-field appearance="outline">
        <mat-label>{{Label}}</mat-label>   
        <input matInput 
            [attr.maxlength] = "MaxLength"
            [value]="Value ? Value : ''"
            [placeholder]="PlaceHolder ? PlaceHolder : ''"
            [type]="type ? type: 'text'"
            (input)="onChange($event.target.value)"
        >
    </mat-form-field>
</div>

Попытка включить этот ответ с естественным стилем ошибки из текстового поля Angular Material, Наследование проверки с использованием ControlValueAccessor в Угловой


person Community    schedule 28.11.2019    source источник
comment
prideparrot.com/blog/archive/2019/2/   -  person VJAI    schedule 02.12.2019
comment
вы можете настроить рабочий стек?   -  person Ron    schedule 02.12.2019
comment
почему этот вопрос был отклонен? Я задал солидный вопрос вместе с моим продуманным и исследованным ответом ниже, ища более оптимальный код,   -  person    schedule 03.12.2019


Ответы (3)


У меня такая же проблема. Я перепробовал все и наконец смог решить с помощью этого метода:

Я добавил этот слушатель в пользовательский компонент. Вы также можете сделать это событие «размытия».

@HostListener('focusout', ['$event.target'])
  onFocusout() {
    this.onTouched();
  }

А также вызов onTouched при установке любого значения.

 writeValue(value: any) {
    this.onTouched();
    this.Value = value ? value : '';
}
person Saloo    schedule 28.11.2019
comment
если кто-то может заставить stackblitz работать для этого, не стесняйтесь вставлять в этот ответ, кажется, очень эффективным, если он может заработать - person ; 01.12.2019
comment
это нельзя использовать в качестве ответа, пока stackblitz или какая-нибудь рабочая демонстрация не попробовали, не сработали - person ; 09.12.2019

Kinda использовала ваш ответ и предоставленную вами ссылку, чтобы придумать это решение:


@Component({
  selector: 'app-custom-input',
  templateUrl: './custom-input.component.html',
  styleUrls: ['./custom-input.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => CustomInputComponent),
    multi: true
  }]
})
export class CustomInputComponent implements OnInit, ControlValueAccessor {
  ...
  _control: NgControl;
  disabled: boolean;

  constructor(@Inject(INJECTOR) private injector: Injector) {
  }

  ngOnInit() {
    this._control = this.injector.get(NgControl);
  }
  
  ...

custom-input.component.html

<div class="input-wrap">
    <mat-form-field appearance="outline">
        <mat-label>{{Label}}</mat-label>   
        <input matInput
            [formControl]="_control.control" // <== this what makes it work
            [attr.maxlength]="MaxLength"
            [placeholder]="PlaceHolder ? PlaceHolder : ''"
            [type]="type ? type: 'text'"
        >
    </mat-form-field>
</div>

Примечание: я не привязываюсь к выводам MatInput (да). директива formControl, которая передает управление в MatInput, делает это за нас.

сделал для вас пример

person Ron    schedule 03.12.2019

Это создаст проверку ошибок из Angular Material

Машинопись:

import { Component, OnInit, Input, EventEmitter, Output, forwardRef, Injector } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR, FormControl, NgForm, FormGroupDirective, NgControl } from '@angular/forms';
import { ErrorStateMatcher } from '@angular/material';

export class CustomFieldErrorMatcher implements ErrorStateMatcher {
  constructor(private customControl: FormControl,private errors:any) { }

  isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
    return this.customControl && this.customControl.touched &&(this.customControl.invalid || this.errors);
  }
}

@Component({
  selector: 'app-input-textbox',
  templateUrl: './input-textbox.component.html',
  styleUrls: ['./input-textbox.component.scss'],
  providers: [
    {
      provide: NG_VALUE_ACCESSOR,
      useExisting: forwardRef(() => InputTextboxComponent),
      multi: true
    }
  ]
})

export class InputTextboxComponent implements OnInit, ControlValueAccessor {
  @Input() MaxLength: string;
  @Input() FocusIn: boolean;
  @Input() Width: string;
  @Input() Value: string;
  @Input() type: string;
  @Input() Label: string;
  @Input() Hint: string;
  @Input() PlaceHolder: string;
  @Output() saveValue = new EventEmitter();
  @Output() onStateChange = new EventEmitter();
  @Input() errors: any = null;
  disabled: boolean;
  control: FormControl;

  constructor(public injector: Injector) {}

  ngOnInit(){}

  ngAfterViewInit(): void {
    const ngControl: NgControl = this.injector.get(NgControl, null);
    if (ngControl) {
      setTimeout(() => {
        this.control = ngControl.control as FormControl;
      })
    }
  }

  saveValueAction(e) {
    this.saveValue.emit(e.target.value);
  }

  //control value accessor init
  writeValue(value: any) {
    this.Value = value ? value : '';
  }

  onChange(e) {
    this.Value = e;
  }

  onTouched() {
    this.onStateChange.emit();
  }

  registerOnChange(fn: any) { this.onChange = fn; }

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

  setDisabledState(isDisabled) { this.disabled = isDisabled; }

  errorMatcher() {
    return new CustomFieldErrorMatcher(this.control,this.errors)
  }

  readonly errorStateMatcher: ErrorStateMatcher = {
    isErrorState: (ctrl: FormControl) => (ctrl && ctrl.invalid)
  };

}

HTML

<div class="input-wrap">
    <mat-form-field>
        <mat-label>{{Label}}</mat-label>   
        <input 
            matInput 
            [attr.maxlength] = "MaxLength"
            [value]="Value ? Value : ''"
            [placeholder]="PlaceHolder ? PlaceHolder : ''"
            [type]="type ? type: 'text'"
            [ngModel]="Value" 
            [errorStateMatcher]="errorMatcher()"

            (input)="onChange($event.target.value)"
            (blur)="onTouched()"
            (change)="saveValueAction($event)"
            (ngModelChange)="Value=$event;onChange($event)"
        >
        <mat-hint>{{Hint}}</mat-hint>
    </mat-form-field>
</div>
person Community    schedule 30.11.2019