I have a simple setup where a recipe item can trigger the display of a modal component with the recipe item's data. Here is the code.
// modal.service.ts
import { RecipeModel } from './recipe.model'
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable()
export class ModalService {
selectedRecipe = new Subject<RecipeModel>();
constructor() {}
}
// recipe-item.component.ts
import { Component, OnInit, Input } from '@angular/core';
import { RecipeModel } from '../../recipe.model';
import { ModalService } from '../../modal.service';
@Component({
selector: 'app-recipe-item',
templateUrl: './recipe-item.component.html',
styleUrls: ['./recipe-item.component.css']
})
export class RecipeItemComponent implements OnInit {
isFavourite: boolean = false;
@Input() recipeInstance: RecipeModel;
cardText: string;
constructor(private modalService: ModalService) { }
ngOnInit(): void {
this.cardText = this.recipeInstance.ingredients.slice(0, 3).map(elem => elem.name).join(', ');
this.cardText = this.cardText + '...and ' + (this.recipeInstance.ingredients.length - 3) + ' more';
}
showRecipeDetail() {
this.modalService.selectedRecipe.next(this.recipeInstance);
}
}
// recipe-detail.component.ts
import { Component, OnInit, Input, ViewChild } from '@angular/core';
import { NgbModal } from '@ng-bootstrap/ng-bootstrap';
import { RecipeModel } from '../recipe.model';
import { ModalService } from '../modal.service';
@Component({
selector: 'app-recipe-detail',
templateUrl: './recipe-detail.component.html',
styleUrls: ['./recipe-detail.component.css']
})
export class RecipeDetailComponent implements OnInit {
@Input() selectedRecipe: RecipeModel;
@ViewChild('mymodal') modal: any;
constructor(private ngModalService: NgbModal, private modalService: ModalService) {}
ngOnInit(): void {
this.modalService.selectedRecipe.subscribe(val => {
this.selectedRecipe = val;
if (val) {
this.ngModalService.open(this.modal, {size: 'lg', scrollable: true});
}
})
}
}
When I go to /recipes for the first time, I can click read more on the recipe item cards shown there, and the recipe detail modal component pops up only once, as usual. When I change the route, and then come back, the number of modals gets doubled, and then tripled if I route again. This happens because I am calling the modal popup inside the ngOnInit() which gets called every time the /recipes is reinitialized.
How can I fix this problem?
Edit: Here is the Github link if you want to take a look at the project structure.
What is happening is that each time RecipeDetailComponent is created it calls ngOnInit() therefore you are subscribing callbacks each time this happens. So you will end with N callbacks registered for that event.
Easier solution is to unsubscribe each time the component is destroyed ngOnDestroy(). https://angular.io/api/core/OnDestroy
Find here a better explanation an another options: Angular/RxJs When should I unsubscribe from `Subscription`