简体   繁体   中英

How to emit an event from child to parent to another child?

I'm having a child component that get some data from a form. And passes that to a parent component via the @Output decorator. Pressing a button triggers getDataFromForm()

export class FormChildComponent {

    @Output() doSomethingWithData: EventEmitter<any> = new EventEmitter<any>()

    ...

    getDataFromForm(){
        ...

        this.doSomethingWithData.emit(form.values);
        
    }
    renderSomething(?data){
       //This needs to be called in anther child after the event got 
       triggered and the data got processed in the parent

    }

}

In the parent component I'm doing some processing with the data, on the button press event in the child. After that I have to render something based on the processed data in another child, which is the same child component type as above.

parent.component.html

<FormChildComponent (doSomethingWithData)="processData($event)">

parent.component.ts

processData($event: object){
    
    doSomething($event);

} 

What's the best practice to pass events and data between children and their parent?

Dealing with inputs and outputs on multiple components can get messy, I prefer to just create a service to emit and subscribe from events.

export type DataType = { name: string };

@Injectable({ providedIn: 'root' })
export class MyService {
  dataSubject = new Subject<DataType>();
}

Just inject the service wherever you need it, call next on the subject to emit the event, and subscribe to the subject where you want to act on the events.

To emit

export class ChildOneComponent {
  data = { name: 'chris' };

  constructor(private myService: MyService) {}

  emit() {
    this.myService.dataSubject.next(this.data);
  }
}

To act on the event

export class ChildTwoComponent {
  subscription = new Subscription();

  constructor(private myService: MyService) {}

  ngOnInit() {
    this.subscription = this.myService.dataSubject.subscribe(this.processData);
  }

  processData(data: DataType) {
    console.log('Got: ', data);
  }

  ngOnDestroy() {
    this.subscription.unsubscribe();
  }
}

Better yet, you can just use the pipe operator and the async pipe to process the data and inject it into your html, no need for subscribing / unsubscribing manually.

export class ChildTwoComponent {
  data = this.myService.dataSubject.pipe(map(this.processData));

  constructor(private myService: MyService) {}

  processData(data: DataType) {
    return 'Hi there ' + data.name;
  }
}
<p>{{ data | async }}</p>

Example: https://stackblitz.com/edit/angular-ivy-al3yqs?file=src/app/child-two/child-two.component.ts


I know you said the child components are the same type, so I'm not sure exactly what your code looks like, considering the components are doing two different things. You might need to elaborate.

Is it two components that are subscribed to eachother? Is it one way where one component is subscribed to the other but not vise versa? Is it a list of components that are subscribed arbitrarily to each other? etc.

You should just define an variable on the child and use @Input() decorator. Eg: @Input() prop: YourType; .

Then you can pass data from the parent to the child by using:

<FormChildComponent [prop]="dataToPassToChild" (doSomethingWithData)="processData($event)">

This is usually the best practice in order to pass data from parent to child components. The other way around was already included in your question (using @Output() decorator).

There are some cases where you want to extract some properties in an independent state. You should use a service in order to achieve this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM