如何在嵌套组件的表单中实现自动表单验证?
How to implement automatic form validation in form with nested component?
考虑以下在 Angular 4 应用程序的组件中的简单示例。它显示了一个带有两个输入字段的简单 HTML 表单。一个输入字段是直接实现的,第二个是在子组件中:
<form #personForm="ngForm">
<input type="text" required name="firstname [(ngModel)]="firstname"/>
<app-smart-input required [(model)]="lastname"></app-smart-input>
<button [disabled]="personForm.valid === false">Send</button>
</form>
子组件定义如下:
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
@Component({
selector: "app-smart-input",
templateUrl: "./smart-input.component.html",
styleUrls: ["./smart-input.component.css"]
})
export class SmartInputComponent {
////////////////
// PROPERTIES //
////////////////
@Input() model: string;
@Output() modelChange: EventEmitter<string> = new EventEmitter<string>();
@Input("required") required: boolean = false;
/////////////
// METHODS //
/////////////
updateChanges() {
this.modelChange.emit(this.model);
}
}
与以下 html:
<input type="text" [required]="required" [(ngModel)]="model" (ngModelChange)="updateChanges()" />
现在更新模型工作得很好(firstname
和 lastname
按预期由用户输入定义)。
我想要实现的是按钮被禁用,除非两个字段都被填写。注意 <input>
实现中的 required
标志,因此值不应该是 null/undefined.
但不幸的是,如果 firstname
没有明确定义,该按钮现在仅被禁用。但是表格并不关心 lastname
.
我该如何实现?
注意: 是模拟的,但我使用的是模板驱动形式,而不是反应形式。但它可能会以某种方式进行调整?
如果您希望 SmartInputComponent 作为 Angular 表单的一部分参与,则需要实施 ControlValueAccessor
。
这意味着为您的自定义组件提供一种方式,让更改在它和父表单之间传播。这是一个使用您的 SmartInputComponent 作为基础的实现。
import { Component, OnInit, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'
@Component({
selector: 'app-smart-input',
templateUrl: './smart-input.component.html',
styleUrls: ['./smart-input.component.css'],
providers:[ {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SmartInputComponent),
multi: true
}]
})
export class SmartInputComponent implements ControlValueAccessor {
@Input() model: any;
onChange = (_: any) => {};
onTouched = () => {};
writeValue(obj: any): void {
if (obj !== undefined) {
this.model = obj;
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
//TODO: enabled/disable your input field if you need to
}
updateChanges(val) {
this.onChange(val);
}
updateBlur(){
this.onTouched();
}
}
使用如下模板:
<input type="text" (input)="updateChanges(myTextBox.value)" (blur)="updateBlur()" #myTextBox [value]="model"/>
然后在使用您的组件时,让它以标准控件的形式参与(Angular 为您提供 ControlValueAccessor
实现)。
<form #personForm="ngForm">
<input type="text" required name="firstname" [(ngModel)]="firstname" name="firstName" />
<app-smart-input required [(ngModel)]="lastname" name="secondName"></app-smart-input>
<button [disabled]="personForm.valid === false">Send</button>
{{personForm.value | json}}
</form>
如果 运行 此表单,您会看到 personForm
现在同时捕获了名字和第二个名字值。
另请参阅有关 ControlValueAccessor
的 Angular 文档
考虑以下在 Angular 4 应用程序的组件中的简单示例。它显示了一个带有两个输入字段的简单 HTML 表单。一个输入字段是直接实现的,第二个是在子组件中:
<form #personForm="ngForm">
<input type="text" required name="firstname [(ngModel)]="firstname"/>
<app-smart-input required [(model)]="lastname"></app-smart-input>
<button [disabled]="personForm.valid === false">Send</button>
</form>
子组件定义如下:
import { Component, EventEmitter, Input, OnInit, Output } from "@angular/core";
@Component({
selector: "app-smart-input",
templateUrl: "./smart-input.component.html",
styleUrls: ["./smart-input.component.css"]
})
export class SmartInputComponent {
////////////////
// PROPERTIES //
////////////////
@Input() model: string;
@Output() modelChange: EventEmitter<string> = new EventEmitter<string>();
@Input("required") required: boolean = false;
/////////////
// METHODS //
/////////////
updateChanges() {
this.modelChange.emit(this.model);
}
}
与以下 html:
<input type="text" [required]="required" [(ngModel)]="model" (ngModelChange)="updateChanges()" />
现在更新模型工作得很好(firstname
和 lastname
按预期由用户输入定义)。
我想要实现的是按钮被禁用,除非两个字段都被填写。注意 <input>
实现中的 required
标志,因此值不应该是 null/undefined.
但不幸的是,如果 firstname
没有明确定义,该按钮现在仅被禁用。但是表格并不关心 lastname
.
我该如何实现?
注意:
如果您希望 SmartInputComponent 作为 Angular 表单的一部分参与,则需要实施 ControlValueAccessor
。
这意味着为您的自定义组件提供一种方式,让更改在它和父表单之间传播。这是一个使用您的 SmartInputComponent 作为基础的实现。
import { Component, OnInit, Input, Output, EventEmitter, forwardRef } from '@angular/core';
import { ControlValueAccessor, NG_VALUE_ACCESSOR} from '@angular/forms'
@Component({
selector: 'app-smart-input',
templateUrl: './smart-input.component.html',
styleUrls: ['./smart-input.component.css'],
providers:[ {
provide: NG_VALUE_ACCESSOR,
useExisting: forwardRef(() => SmartInputComponent),
multi: true
}]
})
export class SmartInputComponent implements ControlValueAccessor {
@Input() model: any;
onChange = (_: any) => {};
onTouched = () => {};
writeValue(obj: any): void {
if (obj !== undefined) {
this.model = obj;
}
}
registerOnChange(fn: any): void {
this.onChange = fn;
}
registerOnTouched(fn: any): void {
this.onTouched = fn;
}
setDisabledState?(isDisabled: boolean): void {
//TODO: enabled/disable your input field if you need to
}
updateChanges(val) {
this.onChange(val);
}
updateBlur(){
this.onTouched();
}
}
使用如下模板:
<input type="text" (input)="updateChanges(myTextBox.value)" (blur)="updateBlur()" #myTextBox [value]="model"/>
然后在使用您的组件时,让它以标准控件的形式参与(Angular 为您提供 ControlValueAccessor
实现)。
<form #personForm="ngForm">
<input type="text" required name="firstname" [(ngModel)]="firstname" name="firstName" />
<app-smart-input required [(ngModel)]="lastname" name="secondName"></app-smart-input>
<button [disabled]="personForm.valid === false">Send</button>
{{personForm.value | json}}
</form>
如果 运行 此表单,您会看到 personForm
现在同时捕获了名字和第二个名字值。
另请参阅有关 ControlValueAccessor
的 Angular 文档