2017-09-25 67 views
1

在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()" /> 

现在更新模型完全正常(firstnamelastname由用户输入按预期定义)。

我想实现的是按钮被禁用,除非填写了两个字段。请注意<input>实现中的required标志,因此这些值不应为null/undefined。

但不幸的是,该按钮现在仅在firstname未定义好的情况下被禁用。但表格并不在乎lastname

我该怎么做到这一点?

注意:Angular 2 creating reactive forms with nested components是simular,但我使用模板驱动的形式,而不是反应形式。但它可能会以某种方式适应?

回答

2

如果您希望SmartInputComponent作为Angular Form的一部分参与,则需要实施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"/> 

然后消耗您的组件时,有它参与的形式,如标准控件(角提供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文档