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.