Reputation: 3509
I'm new to Angular and I have a task that I can't seem to find the best approach to do it. I have a parent component which haves 3 child components, which looks like the picture bellow.
When the user clicks on the save button, which is in the parent controller, if the forms are valid then all the form inputs from Child 1 and Child 3, and the list of items from the Child 2, will have to be saved into the database. To the web api, just one call will be made, and one single object will be sent (the object will have some strings and a list of items).
I've read there are multiple ways to communicate between parent and child components, but I am not sure what is the best one in this case. If I use ViewChild, then the parent will have knowledge of the other components so they will be tight coupled. If I use Input/Output, then I must find a way to emit all the values on click but only if the forms are valid.(something I haven't found how to do) If I use services, I don't know it will help with form validity and returning the form values?
For these cases when the parent communicates with multiple sub-components and complex objects is there a best approach? Any information or help regarding this topic will be very appreciated.
Upvotes: 2
Views: 1027
Reputation: 6488
It is not the easiest task. But I highly recommend to use Reactive Forms. Then you can create a FormGroup
in your parent component with Angulars FormBuilder
.
form: FormGroup;
constructor(private fb: FormBuilder) {
this.form = this.fb.group({
subform1: this.fb.group({
input1: null,
input2: 'thisisastring'
}),
subform2: this.fb.group({
input1: null,
input2: null
})
})
}
get subform1(): FormGroup {
return this.form.get('subform1') as FormGroup;
}
In your template you now will be able to pass in your child formgroups as Input.
<form [formGroup]="form" (ngSubmit)="functionToSave(form.value)">
<app-subform1 [form]="subform1"></app-subform1>
</form>
In your child component you can now create a form like above
@Input() form: FormGroup;
and use it in your template like this
<div [formGroup]="form">
<input formControlName="input1">
</div>
without the usage of a form
tag. Then in your parent component just create the functionToSave
function to handle all the data. For validation just import Validators
from @angular/forms and use the FormBuilder
like this
this.form = this.fb.group({
subform1: this.fb.group({
input1: [null, Validators.required]
})
})
Upvotes: 1
Reputation: 110
app.component.ts
import { Component, ViewChild } from "@angular/core";
@Component({
selector: "app-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"]
})
export class AppComponent {
title = "app";
@ViewChild("child1Component") child1: any;
@ViewChild("child2Component") child2: any;
@ViewChild("child3Component") child3: any;
save() {
console.log(this.child1.form.value);
console.log(this.child2.form.value);
console.log(this.child3.form.value);
}
}
app.component.html
Parrent
<button type="submit" (click)="save()" [disabled]="!child1.form.valid || !child2.form.valid || !child3.form.valid"> Save </button>
<app-child1 #child1Component></app-child1>
<app-child2 #child2Component></app-child2>
<app-child3 #child3Component></app-child3>
child1.component.ts
import { Component, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'app-child1',
templateUrl: './child1.component.html',
styleUrls: ['./child1.component.css']
})
export class Child1Component implements OnInit {
@ViewChild("child1Form") form: any;
constructor() { }
ngOnInit() {
}
}
child1.component.html
<div class="container">
<h1>Child 1</h1>
<form #child1Form="ngForm">
<div class="form-group">
<input type="text" class="form-control" name="input1" [(ngModel)]="input1" placeholder="Required" required>
</div>
<div class="form-group">
<input type="text" class="form-control" name="input2" [(ngModel)]="input2">
</div>
<div class="form-group">
<input type="text" class="form-control" name="input3" [(ngModel)]="input3">
</div>
<div class="form-group">
<input type="text" class="form-control" name="input4" [(ngModel)]="input4">
</div>
</form>
</div>
Add two child component same as child1.component.ts and child1.component.html
Also, I have added require field validation and based on that save button disabled and enabled.
Upvotes: 0
Reputation: 2256
Your task is not trivial. I suppose that the main point is that it is the big form. Validation issues can appear. Start from Reactive Form, make nested forms by FormGroup and split into subcomponents if it is needed using binding mechanisms. There is a related article Angular2: Building nested, reactive forms
Upvotes: 0
Reputation: 12552
The forms have a value and status changed events, more exactly Observables. You can create component outputs for the child components that will represent this. One for value changed and one for status changed. Whenever they are triggered you emit a new event on the output.
In this way in the parent component you will always have the status and value of all forms all the time and can use with when the user clicks the save button. You could also, for example, disable the button if not all the forms are valid.
Upvotes: 0