In this Angular tutorial, we’re going to discuss How to bind an Observable to the Angular form to return if the form is VALID or INVALID as a user changes the values in Angular 11/10/9/8/7/6/5/4/3/2 application.
During the implementation of Template-driven or Reactive Angular 2+ forms, we may need to get the Form status inside or emit out the validation status through Properties or Emit events outside to some other component.
To achieve this we can subscribe to two available Observable methods available with form object. We’ll be discussing the implementation with examples for both and the differences between them.
By exposing the form Observable we can achieve the following scenarios or use-cases :
- Pass Form validation status to the Parent component.
- Check validation status of Angular form in Parent/ Child or non-relational components.
- Enable/ Disable the page elements based on form valid status.
Let’s get started!
Step 1 – Create Angular Application
Run following npm commands to create the Angular application. Current version we have is 11.
$ ng new angular-forms-app
# ? Would you like to add Angular routing? Yes
# ? Which stylesheet format would you like to use? SCSS
Enter the project directory
$ cd angular-forms-app
Run the application
$ ng serve --open
Step 2 – Create Parent and Child Component
To easily demonstrate the use-cases, we’ll create two new Components by hitting the following npm command inside our project root:
$ ng generate component parent
$ ng generate component child
The above command will create the ParentComponent
and ChildComponent
as shown below:
Update Style
To differentiate the components in the application, add the following SCSS style inside the styles.scss file
app-root {
border: 4px solid blue;
display: block;
margin: 10px;
padding: 10px;
}
app-parent {
border: 4px solid red;
display: block;
margin: 10px;
padding: 10px;
}
app-child {
border: 4px solid green;
display: block;
margin: 10px;
padding: 10px;
}
Add components to show-up
After that, open the app.component.html file, then place Parent and Child component tags in it:
<div>
<app-parent></app-parent>
<app-child></app-child>
</div>
Now, when you run the application, it will look like this:
Add Form in Child Component
Now, we’ll add a simple Template Driven Angular form in the ChildComponent
to get its Validation status inside the ParentComponent
.
Open the child.component.html file then update it with the following code:
<h3>Child Form</h3>
<form #myForm="ngForm" (ngSubmit)="submitForm($event)">
<input type="text" [(ngModel)]="username" required name="username">
<button>Submit</button>
</form>
We have a super simple form with a text input that is required and a button.
Next, open the child.component.ts file to update the Class component as shown below:
// child.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
@ViewChild('myForm', { static: false }) public MyForm: NgForm;
username: string;
constructor() { }
ngOnInit(): void {}
ngAfterViewInit(): void {
this.MyForm.statusChanges.subscribe(res => {
console.log('statusChanges', { res }); // Return VALID or INVALID
})
this.MyForm.valueChanges.subscribe(res => {
console.log('valueChanges', { res }); // Return values of the form
})
}
submitForm(e) {
console.log(this.MyForm.valid);
}
}
Note: Make sure to add the
FormsModule
in app.module.ts file to use forms in the Angular application.
In the component class, we have used @ViewChild
decorator to get form instance we have in the HTML template. The most important thing to notice here is, we are exposing two form of Observable methods statusChanges
and valueChanges
The subscription to the statusChanges
method will return the status of the form by emitting the VALID or INVALID text whenever a form control value changes.
The valuesChanges
also triggers whenever the value of form is changed, but instead of providing the status, we get values object of all form controls.
One more thing to notice is, we have these subscriptions inside the ngAfterViewInit
hook of the component which is called after ngAfterContentInit when the component’s view has been initialized.
Pass form Status from Child to the Parent component
To pass the form status from Child to Parent component, we can simply use @Input and @Output decorators to emit the current form status value.
Update the app.component.html file, to add a property binding and output function binding as shown below:
<app-parent [FormStatus]="formStatus"></app-parent>
<app-child (getFormStatus)="onGetFormStatus($event)"></app-child>
In the app.component.ts file, we’ll add the definition of onGetFormStatus
method to pass it to Parent component as input property:
export class AppComponent {
formStatus: string;
constructor() { }
ngOnInit() { }
onGetFormStatus(status: string) {
this.formStatus = status;
}
}
Update Child Component
In the ChildComponent
we’ll emit the form status as an Output event. Open the child.compoennt.ts file and update as shown below:
// child.component.ts
import { Component, EventEmitter, OnInit, Output, ViewChild } from '@angular/core';
import { NgForm } from '@angular/forms';
@Component({
selector: 'app-child',
templateUrl: './child.component.html',
styleUrls: ['./child.component.scss']
})
export class ChildComponent implements OnInit {
@ViewChild('myForm', { static: false }) public MyForm: NgForm;
username: string;
@Output() getFormStatus = new EventEmitter();
constructor() { }
ngOnInit(): void { }
ngAfterViewInit(): void {
this.MyForm.statusChanges.subscribe(res => {
this.getFormStatus.emit(res); // Emit form status VALID or INVALID
})
this.MyForm.valueChanges.subscribe(res => {
console.log('valueChanges', { res }); // Return values of the form
})
}
submitForm(e) {
console.log(this.MyForm.valid);
}
}
Inside the statusChanges
subscription, we’re directly emitting the changed value.
Update Parent Component
Inside the ParentComponent
, we just need to catch the emitted values from App component method and get it as input property inside the FormStatus
variable.
export class ParentComponent implements OnInit {
@Input() FormStatus: string;
constructor() { }
ngOnInit(): void {
}
}
And in the Parent template show the status
<h3>Child Form Status: {{FormStatus}}</h3>
That’s it
Now run the application to show live form status from Child form in the Parent component.
Conclusion
We’ve discussed how to use Form object to use the statusChanges
and valueChanges
Observable methods to pass the form status from one component to other. We can easily subscribe to these exposed methods in Template as well as Reactive Angular forms.
If we have a non-relational component, then we can easily create a service with Behaviour Subject and get form status to any one or more components. We’ll discuss the service approach in the next article.