jcarapia
jcarapia

Reputation: 640

How to disable button in parent form when child form is invalid in Angular

I want to disable a button on the parent form when the nested child form is invalid.

This is the parent form component:

<hello name="{{ name }}"></hello>
<h2>Complex form with address component</h2>
<form #myForm="ngForm">
  <div>
    <label>Firstname:</label>
    <input type="text" name="firstName" ngModel>
  </div>
  <div>
    <label>Lastname:</label>
    <input type="text" name="lastName" ngModel>
  </div>
  <address></address>
</form>

<div>
  Root group valid: {{ myForm.valid }}
</div>
<br>

<!-- I want to disable this button --> 
<button [disabled]="!myForm.controls.address.valid">
  Submit Address Only
</button>

This is the child address form component:

import { Component } from '@angular/core';
import { ControlContainer, NgForm } from '@angular/forms';

@Component({
  selector: 'address',
  template: `
    <fieldset ngModelGroup="address" #group="ngModelGroup">
      <div>
        <label>Zip:</label>
        <input type="text" name="zip" ngModel>
      </div>
      <div>
        <label>Street:</label>
        <input type="text" name="street" ngModel>
      </div>
      <div>
        <label>City:</label>
        <input type="text" name="city" ngModel required>
      </div>
    </fieldset>
    Child group valid: {{ group.valid }}
  `,
  viewProviders: [ { provide: ControlContainer, useExisting: NgForm } ]
})
export class AddressComponent  {}

I have attempted [disabled]="!myForm.controls.address.valid" but get an error stating that address is undefined

Here is a stackblitz https://stackblitz.com/edit/angular-jmdawu?file=app%2Fapp.component.html

Upvotes: 0

Views: 2210

Answers (2)

Eliseo
Eliseo

Reputation: 57971

in this case the "simplest way" is to have a reference to the child using ViewChild

//in parent
@ViewChild(AddressComponent) addressComponent:AdressComponent

//or if you use in your .html a template reference
// see the "#addressRef"
// <address #addressRef></address>

@ViewChild("addressRef") addressComponent:AddressComponent

So, in parent you has access to all the variables and methods in child, so you can use in parent html

<button [disabled]="!addressComponent.group.valid?true:null">submit</button>

Upvotes: 0

user6749601
user6749601

Reputation:

You can work with the event emitter.

Child Component

TS

// your instance of the FormGroup
@ViewChild('group') group: FormGroup;

// emits the formGroup state to the parent
@Output() groupIsValid: EventEmitter<boolean> = new EventEmitter<boolean>();


ngOnInit() {
    // subscribe to the state change event and emit the current state to the parent
    this.group.statusChanges().subscribe(() => {
        this.groupIsValid.emit(this.group.valid);
    });
}

Parent Component

TS

Here we define a method that gets called when the event occures and a variable, that holds the state.

// initially set to false
childComponentIsValid: false;

// gets called by event emitter
onChildComponentEvent(value: boolean): void {
    this.childComponentIsValid = value;

    console.log('current child component validity state is: ' + this.childComponentIsValid);
}

HTML

Here we get the event from the child component by adding the parameter to the HTML-Tag

<address (groupIsValid)="onChildComponentEvent($event)"></address>

And here you use the variable.

<button [disabled]="!childComponentIsValid">
    Submit Address Only
</button>

That's it.

Upvotes: 2

Related Questions