0
votes

I have a ParentComponent and it contains a number of ChildComponents. The ChildComponent exposes an EventEmitter via @Output(). I use that to emit some data that is gathered from a form contained by the ChildComponent.

The ParentComponent has the means to asynchronously save the data to my backend server but the problem is, I need to somehow send back to the ChildComponent the results of the save operation so that it may close the form or show validation errors.

I'm thinking about creating some sort of Subject in my child component and emitting that together with the actual data and on the parent side I would get the emitted stuff, use the data to send to the server, and use the Subject to publish data back to the child component but it seems awfully convoluted and ugly.

If I were to use an @Input on the child component in order to receive updates for this from the ParentComponent then the parent would have to know which of the children emitted the data and that doesn't seem nice.

What nice solutions are there to ensure async bidirectional communication between parent and child components?

2
Are you using reactive forms?Antoniossss

2 Answers

0
votes

What I would do is that I would use Reactive Forms so parent would define whole structure of data model encoded as set of fields (groups and arrays are available as well). Then using plain reactive form API it is possible to detect changes in any part of form and get complete form value eg. for sending.

In case of backend validation errors, parent component will simpy setError or related fields of the form - children will have to be responsible for displaying/handling those errors - so it is just the same approach you want to take.

Using such approach, you will only have to pass form groups (or even single control of the form) to the child component that would be responsible for presentation of the input. No additional events are required as parent will be notified about data changes in the form out of the box trough reactive forms API.

0
votes

I would use subjects but not in components, to keep component clean use a service with 2 Subjects, one the children will use to send data to parent and will subscribe to other one, that the parent will use to send data back to children. Let's create the BidirectionalService

import { Injectable } from "@angular/core";
import { Subject } from "rxjs";

@Injectable({providedIn: 'root'})
export class BidirectionalService {
    public p2cSubject = new Subject<{statusCode: number, msg: string}>(); //parent to 
child
    public c2pSubject = new Subject<{id: number, name: string}>(); //child to parent
}

Here is the child:

import { Component, OnInit } from '@angular/core';
import { BidirectionalService } from '../services/bidirectional.service';

@Component({
  selector: 'app-child1',
  templateUrl: './child1.component.html',
  styleUrls: ['./child1.component.css']
})
export class Child1Component implements OnInit {

  statusFromParent: number;

  constructor(private bs: BidirectionalService) { }

  ngOnInit(): void {
    this.bs.p2cSubject.subscribe((o) => {
      this.statusFromParent = o.statusCode;
    })
  }

  onSubmit1(input1: HTMLInputElement) {
    this.bs.c2pSubject.next({id: 100, name: input1.value});
  }

}

And the parent component:

import { Component, OnInit } from '@angular/core';
import { BidirectionalService } from './services/bidirectional.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = '';
  dataToBePosted: any;

  constructor(private bs: BidirectionalService) {}

  ngOnInit(): void {
    this.bs.c2pSubject.subscribe((o) => {
      this.dataToBePosted = o;
      setTimeout(() => {
        this.bs.p2cSubject.next({statusCode: 200, msg: 'success!'});
     }, 5000); //async
    })
  }
}