Angular 材质中具有错误验证的 ControlValueAccessor
Posted
技术标签:
【中文标题】Angular 材质中具有错误验证的 ControlValueAccessor【英文标题】:ControlValueAccessor with Error Validation in Angular Material 【发布时间】:2020-03-23 23:06:18 【问题描述】:我正在尝试在自定义材质输入文本框中应用带有 ControlValueAccessor 的错误验证样式。自从应用此自定义组件以来,formControlName/FormBuilders 的所有红色边框验证状态都不会显示,对于必需的 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 文本框的自然错误样式结合起来, Inheriting validation using ControlValueAccessor in Angular
【问题讨论】:
prideparrot.com/blog/archive/2019/2/… 你能设置一个有效的 stackblitz 吗? 为什么这个问题被否决了?我提出了可靠的问题,以及我在下面经过深思熟虑的研究答案,寻找更优化的代码, 【参考方案1】:我遇到了同样的问题。我尝试了所有,然后终于可以使用这种方法解决:
我在自定义组件上添加了这个监听器。你也可以做'blur'事件。
@HostListener('focusout', ['$event.target'])
onFocusout()
this.onTouched();
并且在设置任何值时也会调用 onTouched。
writeValue(value: any)
this.onTouched();
this.Value = value ? value : '';
【讨论】:
如果有人可以让 stackblitz 为这个工作,请随意粘贴到这个答案中,如果它可以正常工作,似乎非常有效 这不能用作答案,直到 stackblitz 或一些工作演示,尝试过,没有工作【参考方案2】: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 的输出(是的)。将控件传递给 MatInput 的 formControl 指令为我们处理。
为你做了一个example
【讨论】:
如果strictTemplate
开启(在 Angular 12 中)将不起作用。由于_control.control
返回AbstractControl
。虽然您可以控制您需要将方法转换为ngAfterViewInit
【参考方案3】:
这将从 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>
【讨论】:
以上是关于Angular 材质中具有错误验证的 ControlValueAccessor的主要内容,如果未能解决你的问题,请参考以下文章
Angular 5,具有 3 种状态的 Angular 材质复选框(选中、未选中、不确定)
如何添加具有功能的 Angular 材质自定义 mdToast?
Angular 12:架构验证失败并出现以下错误:数据路径“”不应具有其他属性(inlineStyleLanguage)
Angular 材质 Snackbar 配置与自定义 panelClass 配置,用于错误、成功、警告消息
Angular 材质扩展面板错误 - TypeError: this.driver.containsElement is not a function