Reputation: 205
I have this structure:
I want to subscribe to an object in the service whenever the compare-product is loaded. It seems fine at first. But, when I hit the back button to the product-search, then load the compare-product again, it subscribes twice. When I go back and load again, it is called three times. Back, load again, called four times, and so on.
these are the codes:
service:
//product.service
.......
private checkedSrc = new BehaviorSubject([]);
currentChecked = this.checkedSrc.asObservable();
.......
setChecked(checked: string[]){
this.checkedSrc.next(checked);
}
resetChecked(){
this.checkedSrc.next([]);
}
.......
product-search component:
......
compare(){
this.products_valid = true;
this.productSvc.setChecked(this.checked);
this.router.navigate(['compare']);
}
......
compare-product component:
...
products: string[];
isLoading: boolean = true;
constructor(
private productSvc: ProductsService
) { }
ngOnInit() {
this.productSvc.currentChecked.subscribe(p => {this.products = p; console.log(this.products)})
}
I have tried it without navigating to the compare component. When I first call the compare function it subscribes once, call the compare again it subscribes twice, call again it subscribes three times, and so on.
......
compare(){
this.products_valid = true;
this.productSvc.setChecked(this.checked);
this.productSvc.currentChecked.subscribe(p =>{console.log(p);})
}
......
Button that calls it:
<div class="row">
<div class="col-md-6">
<button class="btn btn-primary" style="margin-top: 20px;" (click)="compare()">Compare</button>
</div>
</div>
I also have tried to reset the object with resetChecked() everytime the compare method called, but still the same...
Upvotes: 5
Views: 8996
Reputation: 6811
You need to unsubscribe to the observable when the component is destroyed. Each time you load the component, you have 1 subscription.
Upvotes: 9
Reputation: 7135
In Angular 6, if you're using the ngOnDestroy approach remember to use a reference to the subscription.
import { Subscription } from 'rxjs';
export class MyComponent implements OnInit, OnDestroy {
private subscription: Subscription //import from rxjs
constructor(private myService: MyService){}
ngOnInit() {
this.subscription = this.myService.myMethod.subscribe(...);
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
}
If, in ngOnDestroy, you call unsubscribe directly on this.myService.myMethod you'll end up getting a "ObjectUnsubscribedError" exception.
Upvotes: 2
Reputation: 26828
Whenever you call subscribe
the source - checkSrc
in this case - gets informed that a new subscriber wants to get data. The source doesn't know when you leave the page, it still keeps track of one subscriber. When you return subscribe
is called again and now the source has two subscribers.
You have several options to solve the problem. The first one is to unsubscribe in the ngOnDestroy
method:
subscription;
ngOnInit() {
this.subscription = this.productSvc.currentChecked.subscribe(p => {this.products = p; })
}
ngOnDestroy() {
this.subscription.unsubscribe();
}
A better option is to use the async
pipe.
ngOnInit() {
this.products = this.productSvc.currentChecked();
}
And in your HTML:
<ul>
<li *ngFor="let product of products | async">{{product.name}}</li>
</ul>
As you can see this.products
no longer refers to the products but the stream of products. In order to use in in your template you add the async
pipe. On of the advantages is that you don't need to subscribe and unsubscribe yourself.
Upvotes: 8