简体   繁体   中英

Angular2/4 unsubscribe observable in a component which never be destroyed

I am new to Angular4. so sorry for the basic question and my poor English. I will try my best to describe my problem.

I am now developing a websites using observable services to share message.

But I got a problem, here is my example file structure . and the working Plunker .

I had "Header Component" received message from "User Component".

Header Component:

import { Component, OnInit, OnDestroy} from '@angular/core';
import { Subscription } from 'rxjs/subscription';
import { SharedService } from "./sharedService";

@Component({
  selector: 'shared-header',
  template: `<div class="my-header">
              <span>This is header </span>
              <span class="right-align">Recieve Data:{{received}}</span>  
            </div>`,
})

export class HeaderComponent implements OnInit, OnDestroy{

  private subscription: Subscription;
  private received = "none";

  constructor(private sharedService : SharedService) {}

  ngOnInit(){
    this.subscription = this.sharedService.shareAction$.subscribe(result => {
      if(result){
        this.received = result;
      } 
    };
  }

  ngOnDestory(){
    this.subscription.unsubscribe();
  }
}

User Component:

import { Component, OnInit, OnDestroy} from '@angular/core';
import { Subscription } from 'rxjs/subscription';
import { SharedService } from "./sharedService";

@Component({
  selector: 'shared-header',
  template: `<div class="my-header">
              <span>This is header </span>
              <span class="right-align">Recieve Data:{{received}}</span>  
            </div>`,
})

export class HeaderComponent implements OnInit, OnDestroy{

  private subscription: Subscription;
  private received = "none";

  constructor(private sharedService : SharedService) {}

  ngOnInit(){
    this.subscription = this.sharedService.shareAction$.subscribe(result => {
      if(result){
        this.received = result;
      } 
    };
  }

  ngOnDestory(){
    this.subscription.unsubscribe();
  }
}

and "Widget Component" received message from "Product Component".

Widget Component:

import { Component, OnInit, OnDestroy} from '@angular/core';
import { Subscription } from 'rxjs/subscription';
import { SharedService } from "./sharedService";

@Component({
  selector: 'widget',
  template: `<div>---Widget----</div>
             <div>=={{productName}}==</div>`,
})

export class WidgetComponent implements OnInit, OnDestroy{

  private productName = "none";
  private subscription: Subscription;

  constructor(private sharedService : SharedService) {}

  ngOnInit(){
    this.subscription = this.sharedService.shareAction$.subscribe(result => {
      if(result){
        this.productName = result;
      } 
    };
  }
  ngOnDestory(){
    this.subscription.unsubscribe();
  }
}

Product Component:

import { Component, OnInit, OnDestroy} from '@angular/core';
import { SharedService } from "./sharedService";

@Component({
  template: `<h4>This is ProductPage</h4>
             <widget></widget>`,
})

export class ProductComponent implements OnInit, OnDestroy{

  constructor(private sharedService : SharedService) {}

  ngOnInit(){
    this.sharedService.publishData('NIKE');
  }
}

I create a sharedService to handle observable services. SharedService :

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subscription } from 'rxjs/subscription';

@Injectable()
export class SharedService {

  private shareAction = new BehaviorSubject<any>(null);
  shareAction$ = this.shareAction.asObservable()
  sharedObject: object = {};

  constructor() {}

  publishData(data) {
    console.log('publish------------------------------');
    this.shareAction.next(data);
  }
}

So when clicking "user page", the info on header's right side will show "User Page". And when clicking "Product page", the info below the widget will show "NIKE". But in the same time, the header info also change to "NIKE" which I don't want it happens.

I know I should unsubscribe when the component destroy, But in this case, Header will never be destroyed. So maybe....I should unsubscribe it when the "user Component" destroy, and subscribe again when "user Component" init, but I don't know how to do that, or this idea is bad.

Please give me some instruction, any opinion will be help. Thanks a lot.

I think the problem is that you are using a single shareAction$ to do too many things. Why don't you make one Subject for header info and another for widget?

Also, shareAction$ is too broad: Try giving it a more specific name, like headerTitle$ and currentProduct$

import { Injectable } from '@angular/core';
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Subscription } from 'rxjs/subscription';

@Injectable()
export class SharedService {

    private headerTitle = new BehaviorSubject<string>(null);
    headerTitle$ = this.headerTitle.asObservable();

    private currentProduct = new BehaviorSubject<any>(null);
    currentProduct$ = this.currentProduct.asObservable();

    constructor() {}

    setHeaderTitle(title:string):void {
        this.headerTitle.next(title);
    }
    setCurrentProduct(product:any):void {
        this.currentProduct.next(product);
    }
}

This way, neither of the Header component nor the Widget component need to know when to subscribe/unsubscribe: They just subscribe when they get created and unsubscribe when they get destroyed.

Also, consider avoiding any as types: It will save you a lot of problems when your aplication is big enough, because if there's some change that provokes missmatching types, the compiler will tell you that there's something wrong.

And do you need for CurrentProduct to be globally shared through a service? If you only use it in Product component - Widget component maybe it's enough by passing it through component params.

Use angular 2+ built in mechanism ( | async), it automatically unsubscribe.

some.component.ts:

this.heroes = this.heroService.getHeroes();

some.component.html:

<li *ngFor="let hero of heroes | async" (click)="selectedHero=hero">
    <span class="badge">{{hero.id}}</span> {{hero.name}}
</li>

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