I have used the passing of values between Angular components several times correctly (Input/Output), but I found the following particular situation and I cannot find the solution:
I have two components:
- home.component (parent component)
The parent component, which in turn, contains another navigation component between tabs obtained from an external library, which is the one that loads the child component in a tab: "task.component":
html file:
<div>
<app-tabs [tabs]="tabs" [activeTab]="activeTab" (id)="onId($event)"></app-tabs>
</div>
ts file:
onId($event) {
// $event would contain the id obtained in the child component
}
- tasks.component (child component)
The child component contains the call from a button to call the parent component:
html file:
<button type="button" (click)="play('task/play', 30)">Play</button>
ts file:
@Output() id= new EventEmitter<number>();
play(url: string, id: number) {
this.tasksService.playTask(url).subscribe(
response => {
// From here, I need to communicate with the parent and access a method that is in the parent.
// this.id.emit(id);
},
err => {
}
);
}
In other cases, I pass parameters from the child to the parent without problems because I use my own components, but when using an intermediate tab component I do not get it. (is mandatory to use the tab component). I think it's logical that it doesn't work because the "Output" has to be passed directly to the component "task.component". That is, I cannot pass the "Output" "id" to the "app-tabs" component.
How could I do it?
CodePudding user response:
It seems that child component (or grandchild component) is contained in a child route.
In this case, one solution could be to use a shared service to send notification from child component to parent. (or any other component inside application).
To do that, we need an injectable service (or integrated into an existing service) :
in notification.service.ts
:
@Injectable()
export class NotificationService {
_taskPlayed$ = new Subject<number>();
get taskPlayed$() {
return this._taskPlayed$.asObservable();
}
taskPlayed(id: number) {
this._taskPlayed$.next(id);
}
}
Be sure to add this service in module providers.
Then, we can inject service in child (or any component), and call taskPlayed
method in order to emit a new value/event in observable.
So, in child.component.ts
:
export class ChildComponent {
constructor(private notificationService: NotificationService) {}
playTask() {
this.notificationService.taskPlayed(1);
}
}
Same thing, we inject service in parent (or any component in app), and subscribe to taskPlayed$
observable from service.
(be sure to unsubscribe when component is destroyed).
So, in parent.component.ts
:
export class ParentComponent implements OnInit, OnDestroy {
subscription!: Subscription;
constructor(private notificationService: NotificationService) {}
ngOnInit(): void {
this.subscription = this.notificationService.taskPlayed$.subscribe((id) => {
// do something with id...
....
});
}
ngOnDestroy(): void {
this.subscription?.unsubscribe();
}
}
In this example, only id
value is sent via Observable
, but we can also pass a more complex object.
export class NotificationService {
_taskPlayed$ = new Subject<{ id: number, title: string }>();
get taskPlayed$() {
return this._taskPlayed$.asObservable();
}
taskPlayed(task: {id: number, title: string}) {
this._taskPlayed$.next(task);
}
}