Reputation: 19
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?
Upvotes: 0
Views: 4676
Reputation: 10954
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.
Upvotes: 1
Reputation: 3570
You should just define an variable on the child and use @Input()
decorator.
E.g.: @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.
Upvotes: 0