Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ObjectUnsubscribedError when trying to prevent subscribing twice

I have a Service and a component that uses it:

  • PagesService
  • PagesListComponent

In the PagesService I have an array of Pages. I notify changes in the array via a BehaviorSubject which both of them are subscribed to.

The PagesService are provided at bootstrap, to have just one instance shared. That's because I need to keep the array, instead of downloading the pages everytime they are needed.

The code is the following:

pages.service.ts

import { Injectable } from '@angular/core'; import { BehaviorSubject } from 'rxjs/Rx'; import { Http, Response } from '@angular/http';  import { Page } from './../models/page';  @Injectable() export class PagesService {      public pages$: BehaviorSubject<Page[]> = new BehaviorSubject<Page[]>([]);     private pages: Page[] = [];      constructor(private http: Http) { }      getPagesListener() {         return this.pages$;     }     getAll() {         this.http.get('/mockups/pages.json').map((res: Response) => res.json()).subscribe(             res => { this.resetPagesFromJson(res); },             err => { console.log('Pages could not be fetched'); }         );     }      private resetPagesFromJson(pagesArr: Array<any>) {         // Parses de Array<any> and creates an Array<Page>         this.pages$.next(this.pages);     } } 

pages_list.component.ts

import { Component, OnInit } from '@angular/core'; import { Router } from '@angular/router-deprecated'; import { BehaviorSubject } from 'rxjs/Rx';  import { PagesService } from '../../shared/services/pages.service'; import { GoPage } from '../../shared/models/page';  @Component({     moduleId: module.id,     selector: 'go-pages-list',     templateUrl: 'pages_list.component.html',     styleUrls: ['pages_list.component.css'] }) export class PagesListComponent implements OnInit {     pages$: BehaviorSubject<GoPage[]>;     pages: GoPage[];     constructor(private pagesService: PagesService, private router: Router) { }      ngOnInit() {         this.pages$ = this.pagesService.getPagesListener();         this.pages$.subscribe((pages) => { this.pages = pages; console.log(pages) });         this.pagesService.getAll();     }     ngOnDestroy() {         this.pages$.unsubscribe();     } } 

This works fine the first time, both the subscription onInit and de unsubscription onDestroy. But when I return to the list and try to subscribe again (to fetch the current value of pages[] and listen for future changes), it fires the error EXCEPTION: ObjectUnsubscribedError.

If I don't unsubscribe, everytime I enter to the list, a new subscription is stacked, and all of them are fired when a next() is received.

like image 961
AlvYuste Avatar asked Jun 28 '16 16:06

AlvYuste


People also ask

Does Take 1 automatically unsubscribe?

In such scenarios we can use RxJS take(1) operator which is great because it automatically unsubscribes after the first execution.

Do we need to unsubscribe subject?

You always have to unsubscribe from: Any Observable or Subject you created manually.

How do you destroy an observable?

If the interval doesn't emit up to 9 values before the AppComponent is destroyed, the observable$ subscription will still be open until the interval emits 10 before it is destroyed. So for safety, we add the ngOnDestroy hook to unsubscribe observable$ when the component is destroyed.


1 Answers

I would get the subscription and unsubscribe on it this way and not on the subject directly:

ngOnInit() {   this.pages$ = this.pagesService.getPagesListener();   this.subscription = this.pages$.subscribe((pages) => { // <-------     this.pages = pages; console.log(pages);   });   this.pagesService.getAll(); }  ngOnDestroy() {     this.subscription.unsubscribe(); // <------- } 
like image 159
Thierry Templier Avatar answered Oct 11 '22 10:10

Thierry Templier