23
votes

I'm trying to create my own observable service but after i get the initial data from the service, any updates to the service aren't propagated to any subscribers. Service looks like this:

import { Injectable } from '@angular/core';
import { Observable, BehaviorSubject } from 'rxjs/Rx';

@Injectable()
export class DataService {
  keys : number[] = [4,5,1,3,2];
  private data :BehaviorSubject<number[]> = new BehaviorSubject(this.keys);

  constructor() {};

  public setKey(i:number, val:number) :void {
    this.keys[i]=val;
    this.data.next(this.keys);
  }
  public getData() :Observable<number[]> {
    return new Observable(fn => this.data.subscribe(fn));
  }
  public getKeys() :number[] {
    return this.keys;
  }
}

the component using the service gets the initial data just fine. that component gets its data in the constructor:

constructor(public dataService: DataService) {
    dataService.getData().subscribe(data => {
      console.log("Gotcha!");
      this.data.datasets[0].data = data)
    });
};

which gives one Gotcha in the console log. But after updating the data with setKey(2,3) somewhere else, i was expecting the this.data.next(this.keys); to send data to all subscribers and data would be updated inside that component accordingly. but no data is sent to the subscribers..

i thought i figured the Observables out but please dont be friendly if i'm missing the point here ;) any pointers in the right direction will be greatly appreciated!

2
Sounds like you are providing the service more than once which leads to different components getting different instances. - Günter Zöchbauer
What should be the approach when working in modules. I have a similar service. Some of my components (in different modules) subscribe on an observable in my auth service and receive data when it changes but others don't receive data. Eg. when my login (btn) component emits next on the observer the login components sees the change but my header & sidebar components don't while they are also subscribed, I think it has to do with lazy loading or so, or cache, or some parent child problem.. pff - webmaster
@GünterZöchbauer thanks for this! I was providing the service both as part of the component and in the parent module. It was driving me crazy! - jminuscula
@GünterZöchbauer I literally lost hours on this. Thank you so much. - Tasos Anesiadis
@GünterZöchbauer. Thanks a lot. i'm new to Angular, it helped me to fix the issue quickly. - BALAJI RAJ

2 Answers

41
votes

I faced the same issue at some point. It's likely the reason is that your service is not a singleton, i.e. that every subscriber gets a new instance. Contrary to Angular 1, in A2 services are not singletons.

If you want to have one instance of the service shared by multiple services/components, put it in providers of your parent @Component or @NgModule.

@NgModule({
  declarations: [],
  imports: [],
  bootstrap: [AppComponent],
  providers: [DataService]
})
export class AppModule {
}
12
votes

Every time you emit a value, you should create a new object instead of emitting the same mutated object. That will protect you from change detection issues. It'd be better to always assign a new Object to this.keys when you change it though.

this.data.next([...this.keys]);

You can use asObservable() here

public getData(): Observable<number[]> {
  return this.data.asObservable();
}

Also check for what Günter Zöchbauer said. Are you providing the service as a singleton?