简体   繁体   中英

How to implement automatic form validation in form with nested component?

Consider the following simple example in a component of an Angular 4 application. It shows a simple HTML form with two input fields. One input field is directly implemented, the second is within a child component:

<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>

The child component is defined as follows:

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);
    }

}

with the following html:

<input type="text" [required]="required" [(ngModel)]="model" (ngModelChange)="updateChanges()" />

Now updating the models work perfectly fine ( firstname and lastname are defined by the user input as expected).

What I would like to achieve is that the button gets disabled unless both fields are filled in. Note the required flag in the <input> implementations, so the values should not be null/undefined.

But unfortunately, the button is now only disabled if the firstname is not well defined. But the form doesn't care about the lastname .

How would I achieve this?

Note: Angular 2 creating reactive forms with nested components is simular, but I use a template driven form, not a reactive form. But it can be probably adapted somehow?

You'll need to implement ControlValueAccessor if you want your SmartInputComponent to participate as part of an Angular Form.

This means providing a way for your custom component to have changes propagate between it and the parent form. Here's an implementation using your SmartInputComponent as a basis.

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();
  }
}

with a template like:

<input type="text" (input)="updateChanges(myTextBox.value)" (blur)="updateBlur()" #myTextBox [value]="model"/>

Then when consuming your component, have it participate in form like the standard controls (which Angular provides the ControlValueAccessor implementation for you).

<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>

If run this form you'll see the personForm now has both the first and second name values captured.

Also see the Angular documentation on ControlValueAccessor

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM