ControlValueAccessor with Error Validation in Angular Material

前端 未结 3 780
耶瑟儿~
耶瑟儿~ 2021-01-18 03:37

I am trying to apply Error Validation style with ControlValueAccessor in custom Material Input Textbox. Ever since applying this custom component, all the red border validat

相关标签:
3条回答
  • 2021-01-18 04:29

    I had the same problem. I tried all and then finally could resolve using this method:

    I added this listener on the custom component. You can also do it 'blur' event.

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

    And also calling onTouched when setting any value.

     writeValue(value: any) {
        this.onTouched();
        this.Value = value ? value : '';
    }
    
    0 讨论(0)
  • 2021-01-18 04:32

    This will create error validation from Angular Material

    Typescript:

    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>
    
    0 讨论(0)
  • 2021-01-18 04:37

    Kinda used your answer and the link you provided to come up with this solution:

    
    @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>
    

    Note: I'm not binding to MatInput's outputs (yea). the formControl directive that passes the control to MatInput handles that for us.

    made an example for you

    0 讨论(0)
提交回复
热议问题