【问题标题】:Angular Material <mat-error> not showing in Custom ComponentsAngular Material <mat-error> 未显示在自定义组件中
【发布时间】:2019-04-20 17:43:18
【问题描述】:

所以我一直在做很多研究,但我就是想不通。

我想使用 Angular 材质表单控件制作一个 Textbox 组件

按照这个tutorial,我实现了如下

textbox.component.html

<mat-form-field>
    <input matInput type="text" [placeholder]="placeholder" [(ngModel)]="value" />
    <mat-error>This field is required</mat-error>
</mat-form-field>

textbox.component.ts

import { Component, Input, forwardRef, OnDestroy, ElementRef } from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor, FormControl, NgControl } from '@angular/forms';
import { coerceBooleanProperty } from '@angular/cdk/coercion';
import { MatFormFieldControl } from '@angular/material';
import { Subject } from 'rxjs';
import { FocusMonitor } from '@angular/cdk/a11y';

@Component({
    selector: 'text-box',
    templateUrl: './text-box.component.html',
    styleUrls: ['./text-box.component.scss'],
    providers: [
        {
            provide: NG_VALUE_ACCESSOR,
            useExisting: forwardRef(() => TextBoxComponent),
            multi: true
        },
        {
            provide: MatFormFieldControl,
            useExisting: TextBoxComponent
        }
    ]
})
export class TextBoxComponent implements ControlValueAccessor, MatFormFieldControl<any>, OnDestroy {
    static nextId = 0;

    stateChanges: Subject<void> = new Subject<void>();
    id: string = `text-box-${TextBoxComponent.nextId++}`;
    ngControl: NgControl = null;
    focused: boolean = false;
    empty: boolean;
    shouldLabelFloat: boolean;
    disabled: boolean = false;
    errorState: boolean = false;
    controlType?: string = 'text-box';
    autofilled?: boolean;
    describedBy: string = '';

    @Input()
    get placeholder(): string {
        return this._placeholder;
    }
    set placeholder(value: string) {
        this._placeholder = value;
        this.stateChanges.next();
    }
    private _placeholder: string;

    @Input()
    get required(): boolean {
        return this._required;
    }
    set required(value: boolean) {
        this._required = coerceBooleanProperty(value);
        this.stateChanges.next();
    }
    private _required = false;

    @Input() name: string;

    onChange: any = () => {};
    onTouched: any = () => {};

    isDisabled: boolean = false;

    @Input('value') val: string;
    get value(): any {
        return this.val;
    }
    set value(val: any) {
        this.val = val;
        this.errorState = !val;
        this.onChange(val);
        this.onTouched();
        this.stateChanges.next();
    }

    constructor(private fm: FocusMonitor, private elRef: ElementRef<HTMLElement>) {
        fm.monitor(elRef, true).subscribe(origin => {
            this.focused = !!origin;
            this.stateChanges.next();
        });
    }

    writeValue(value: any): void {
        if (value) {
            this.value = value;
        }
    }
    registerOnChange(fn: any): void {
        this.onChange = fn;
    }
    registerOnTouched(fn: any): void {
        this.onTouched = fn;
    }
    setDisabledState?(isDisabled: boolean): void {
        this.isDisabled = isDisabled;
    }

    setDescribedByIds(ids: string[]): void {
        this.describedBy = ids.join(' ');
    }
    onContainerClick(event: MouseEvent): void {
        if ((event.target as Element).tagName.toLowerCase() != 'input') {
            this.elRef.nativeElement.querySelector('input')!.focus();
        }
    }

    ngOnDestroy(): void {
        this.stateChanges.complete();
        this.fm.stopMonitoring(this.elRef);
    }
}

并且基本上以这样的形式使用它:

<form [formGroup]="someForm" (ngSubmit)="onSubmit()">
    <acs-text-box formControlName="username" [placeholder]="'Form control'"> </acs-text-box>
    <my-button [type]="'submit'">Submit</my-button>
</form>

所以这是我的问题,我正在尝试在 textbox.component.html 模板中进行渲染,但它不起作用

我尝试了几件事,例如在 textbox.component.ts 中设置 errorState = true,但没有任何反应。

我试过了

<mat-form-field>
    <acs-text-box formControlName="username" [placeholder]="'Form control'"> </acs-text-box>
    <mat-error>This field is required</mat-error>
</mat-form-field>

设置 errorState = true 可以正常工作,但是当我将 mat-error 带回文本框组件模板内部时,它不起作用。

有没有办法在文本框组件模板中进行渲染?或者它只是不适用于有角度的材料,我应该以自己的方式实现它?

提前致谢!!

【问题讨论】:

    标签: angular angular-material


    【解决方案1】:

    尝试这样做:

    <mat-form-field>
        <input matInput type="text" [placeholder]="placeholder" [(ngModel)]="value" required #inputValue="ngModel" />
        <mat-error *ngIf="(inputValue.touched && inputValue.invalid)">This field is required</mat-error>
    </mat-form-field>
    

    【讨论】:

      【解决方案2】:

      接受的答案对我没有帮助。

      我认为当自定义组件暴露的表单控件与内部材质输入使用的表单控件不同时会出现问题,因此当材质表单控件不无效时,不会显示错误(请参阅文档下面引用)。

      这有点麻烦,但您可以将 ngControl(暴露的表单控件)传递给内部材料输入的 ErrorStateMatcher。

      official docs中的ErrorStateMatcher解释:

      "默认情况下,当控件无效并且用户已与(触摸)元素交互或已提交父表单时显示这些错误消息。如果您希望覆盖此行为(例如显示错误一旦无效控件变脏或父表单组无效),您可以使用 matInput 的 errorStateMatcher 属性。该属性采用 ErrorStateMatcher 对象的实例。ErrorStateMatcher 必须实现单个方法 isErrorState,该方法采用 FormControl对于这个 matInput 以及父表单,并返回一个布尔值,指示是否应显示错误。"

      可以在文档link 中找到代码示例。

      祝你好运……

      【讨论】:

      • 评论本身很好,但没有说明为什么在自定义表单域上没有显示错误。
      • 我现在添加了一些详细说明
      猜你喜欢
      • 2021-12-18
      • 2020-01-31
      • 1970-01-01
      • 2018-03-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-10-24
      • 1970-01-01
      相关资源
      最近更新 更多