简体   繁体   中英

angular4 ObjectUnsubscribedError

I am implementing an angular4 client application.
I have added some services which does some http request from backend.
Then, it starts to throw following ObjectUnsubscribedError .
Here is a screenshot: 在此输入图像描述

After some research I have found the following warning "An error thrown when an action is invalid because the object has been unsubscribed." from here

I guess the problem caused by ReactJs rxjs/Subject properties. I thought, I might unsubscribe in some wrong please, but my unsubscribing codes are in ngOnDestroy() method which is the exit of the code.
So, I could not find a proper solution or the exact reason of the problem.
Any suggestion or help?
Thanks

A part of my code:

import {Component, Inject, OnDestroy, OnInit} from '@angular/core';
import {Subject} from "rxjs/Subject";

import {APP_CONFIG} from "../../../config/app.config";
import {IAppConfig} from "../../../config/iapp.config";

import {HomelandsService} from "../../../shared/services/homelands.service";
import {SearchService} from "../../../shared/services/search.service";

import {Kelime} from "../../../shared/models/Kelime";
import {City} from "../../../shared/models/homeland/City";
import {County} from "../../../shared/models/homeland/County";
import {Area} from "../../../shared/models/homeland/Area";
import {Neighborhood} from "../../../shared/models/homeland/Neighborhood";
import {HomelandSearchInfo} from "../../../shared/models/HomelandSearchInfo";

@Component({
  selector: 'app-homeland-search-field',
  templateUrl: './homeland-search-field.component.html',
  styleUrls: ['./homeland-search-field.component.css'],
  providers: []
})
export class HomelandSearchFieldComponent implements OnInit, OnDestroy {

  //citiesChanges = new Subject<City[]>();
  countiesChanges = new Subject<County[]>();
  areasChanges = new Subject<Area[]>();
  neighborhoodsChanges = new Subject<Neighborhood[]>();

  cities: City[] = this.appConfig.cities;
  counties: County[];
  areas: Area[];
  neighborhoods: Neighborhood[];

  homelandSearchInfo: HomelandSearchInfo = {
    cityId: null,
    countyId: null,
    areaId: null,
    neighborhoodId: null
  };
  kelimeler: Kelime[];

  constructor(private homelandsService: HomelandsService,
              private searchService: SearchService,
              @Inject(APP_CONFIG) private appConfig: IAppConfig) { }



  ngOnInit() {

    this.countiesChanges = this.homelandsService.countiesChanges;
    this.areasChanges = this.homelandsService.areasChanges;
    this.neighborhoodsChanges = this.homelandsService.neighborhoodsChanges;
    this.countiesChanges.subscribe(
      (counties: County[]) => {
        this.counties = counties;
      }
    );
    this.areasChanges.subscribe(
      (areas: Area[]) => {
        this.areas = areas;
      }
    );
    this.neighborhoodsChanges.subscribe(
      (neighborhoods: Neighborhood[]) => {
        this.neighborhoods = neighborhoods;
      }
    );
    /*this.citiesChanges = this.homelandsService.citiesChanges;
    this.citiesChanges.subscribe(
      (cities: City[]) => {
        this.cities = cities;
        // Loads the cities of Turkey=223
        this.cities = this.homelandsService.getCities().filter((item)=> item.countryId == 223);
      }
    );*/
  }
  ngOnDestroy(){
    this.countiesChanges.unsubscribe();
    this.areasChanges.unsubscribe();
    this.neighborhoodsChanges.unsubscribe();
  }

  // ############################## HOMELAND EVENTs & HELPERs ##############################
  onSelectCity(cityId: number){
    this.homelandSearchInfo.cityId = cityId;
    this.homelandSearchInfo.countyId = null;
    this.homelandSearchInfo.areaId = null;
    this.homelandSearchInfo.neighborhoodId = null;
    //this.counties = this.homelandsService.getCounties().filter((item)=> item.cityId == cityId);
    //this.homelandsService.httpGetCountiesByCityId(cityId);
    this.areas = [];
    this.neighborhoods = [];

  }
  onSelectCounty(countyId: number){
    this.homelandSearchInfo.countyId = countyId;
    this.homelandSearchInfo.areaId = null;
    this.homelandSearchInfo.neighborhoodId = null;
    //this.areas = this.homelandsService.getAreas().filter((item)=> item.countyId == countyId );
    //this.homelandsService.httpGetAreasByCountyId(countyId);
    this.neighborhoods = [];

  }
  onSelectArea(areaId: number){
    this.homelandSearchInfo.areaId = areaId;
    this.homelandSearchInfo.neighborhoodId = null;
    console.log(areaId);
    //this.neighborhoods = this.homelandsService.getNeighborhoods().filter((item)=> item.areaId == areaId );
    //this.homelandsService.httpGetNeighborhoodsByAreaId(areaId);
  }
  onSelectNeighborhood(neighborhoodId: number){
    //this.homelandSearchInfo.neighborhoodId = neighborhoodId;
  }

  onSearch(){
    console.log(this.homelandSearchInfo);

    //1.search servise ilgili bilgileri gönderip sonuçları kelimelerde listelemek.
    this.searchService.searchHomeland(this.homelandSearchInfo);


  }}

====================

import {Inject, Injectable} from '@angular/core';
import {Http} from "@angular/http";
import {Subject} from "rxjs/Subject";

import {APP_CONFIG} from "../../config/app.config";
import {IAppConfig} from "../../config/iapp.config";

import {City} from "../models/homeland/City";
import {County} from "../models/homeland/County";
import {Area} from "../models/homeland/Area";
import {Neighborhood} from "../models/homeland/Neighborhood";
import {Observable} from "rxjs/Observable";
import "rxjs/add/operator/catch";

@Injectable()
export class HomelandsService {

  citiesChanges = new Subject<City[]>();
  countiesChanges = new Subject<County[]>();
  areasChanges = new Subject<Area[]>();
  neighborhoodsChanges = new Subject<Neighborhood[]>();

  cities: City[] = this.appConfig.cities;
  counties: County[] = [];
  areas: Area[] = [];
  neighborhoods: Neighborhood[] = [];

  constructor(private http: Http,
              @Inject(APP_CONFIG) private appConfig: IAppConfig) {
    //this.httpGetCities();
    //this.httpGetCounties();
    //this.httpGetAreas();
    //this.httpGetNeighborhoods();
  }

  getCities(){
    return this.cities.slice();
  }
  getCounties(){
    return this.counties.slice();
  }
  getAreas(){
    return this.areas.slice();
  }
  getNeighborhoods(){
    return this.neighborhoods.slice();
  }


  httpGetCities(){
    let url: string = this.appConfig.backendURL + 'cities/countryId=' + 223;
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const cities: City[] = backendResponse.item;
            console.log(cities);
            return cities;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (cities: City[]) => {
          this.cities = cities;
          this.citiesChanges.next(this.cities.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }

  httpGetCounties(){
    let url: string = this.appConfig.backendURL + 'counties/';
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const counties: County[] = backendResponse.item;
            console.log(counties);
            return counties;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (counties: County[]) => {
          this.counties = counties;
          this.countiesChanges.next(this.counties.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }
  httpGetCountiesByCityId(cityId: number){
    let url: string = this.appConfig.backendURL + 'counties/cityId=' + cityId;
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const counties: County[] = backendResponse.item;
            console.log(counties);
            return counties;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (counties: County[]) => {
          this.counties = counties;
          this.countiesChanges.next(this.counties.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }

  httpGetAreas(){
    let url: string = this.appConfig.backendURL + 'areas/';
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const areas: Area[] = backendResponse.item;
            console.log(areas);
            return areas;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (areas: Area[]) => {
          this.areas = areas;
          this.areasChanges.next(this.areas.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }
  httpGetAreasByCountyId(countyId: number){
    let url: string = this.appConfig.backendURL + 'areas/countyId=' + countyId;
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const areas: Area[] = backendResponse.item;
            console.log(areas);
            return areas;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (areas: Area[]) => {
          this.areas = areas;
          this.areasChanges.next(this.areas.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }

  httpGetNeighborhoods(){
    let url: string = this.appConfig.backendURL + 'neighborhoods/';
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const neighborhoods: Neighborhood[] = backendResponse.item;
            console.log(neighborhoods);
            return neighborhoods;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (neighborhoods: Neighborhood[]) => {
          this.neighborhoods = neighborhoods;
          this.neighborhoodsChanges.next(this.neighborhoods.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }
  httpGetNeighborhoodsByAreaId(areaId: number){
    let url: string = this.appConfig.backendURL + 'neighborhoods/areaId=' + areaId;
    this.http.get(url)
      .map(
        (response) => {
          const backendResponse = response.json();
          if(backendResponse.success){
            const neighborhoods: Neighborhood[] = backendResponse.item;
            console.log(neighborhoods);
            return neighborhoods;
          }else{
            console.error(backendResponse.msg);
            return Observable.throw(backendResponse.msg)
          }
        }
      )
      .catch(
        (error: Response) => {
          console.error(error);
          return Observable.throw(error);
        }
      )
      .subscribe(
        (neighborhoods: Neighborhood[]) => {
          this.neighborhoods = neighborhoods;
          this.neighborhoodsChanges.next(this.neighborhoods.slice());
        },
        (error: Error) => {
          console.error(error);
        }
      )
  }

}

UPDATE:
I have removed the unsubscribe code in my ngOnDestroy( ) method and the error is gone. However, Subject must be unsubscribed somewhere in code. So, the problem is not solved actually. Is there any suggesttion???

Instead of unsubscribing from the Subject itself, create a Subscription and unsubscribe from that. Unsubscribing the Subject or BehaviorSubject closes the observable. Try this:

import { Subject, Subscription } from 'rxjs';

create a propery for the subscription and assign it to the subscribe.

countiesChangesSubscription: Subscription;

this.countiesChangesSubscription = this.countiesChanges.subscribe(...)

then just unsubscribe the subscription instead of the observable itself.

ngOnDestroy() {
  this.countiesChangesSubscription.unsubscribe();
}

Do this for all your subscriptions that you don't want closed on destroy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM