Rocshy
Rocshy

Reputation: 3509

Angular complex components communication

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.

enter image description here

Upvotes: 2

Views: 1027

Answers (4)

Felix Lemke
Felix Lemke

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

Maulik sojitra
Maulik sojitra

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

Victor Shelepen
Victor Shelepen

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

Adrian F&#226;ciu
Adrian F&#226;ciu

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

Related Questions