![](/img/trans.png)
[英]reusable angular reactive form (ControlValueAccessor) & material stepper & asynchronous validation
[英]ControlValueAccessor with Error Validation in Angular Material
我正在嘗試在自定義材料輸入文本框中使用 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 文本框的自然錯誤樣式合並, Inheriting validation using ControlValueAccessor in Angular
我有同樣的問題。 我嘗試了所有,然后終於可以使用此方法解決:
我在自定義組件上添加了這個監聽器。 您也可以執行“模糊”事件。
@HostListener('focusout', ['$event.target'])
onFocusout() {
this.onTouched();
}
並在設置任何值時調用 onTouched。
writeValue(value: any) {
this.onTouched();
this.Value = value ? value : '';
}
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);
}
...
自定義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 指令為我們處理。
為你做了一個例子
這將從 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>
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.