1

I created a project with Angular-CLI 1.0.0-beta.18 (updated yesterday). I'm trying to detect a change in a service, from a component.

  • I create an observable in the service
  • I subscribe to it from the component
  • The subscription never triggers on update :/

I tried to implement the solution from this answer, this Plunkr and this cookbook, no dice.

Here's the service :

import { Injectable } from '@angular/core';
import { ReplaySubject } from 'rxjs/ReplaySubject';
import { Article } from './article';

@Injectable()
export class ArticleService {

    // Placeholder for article's
     articles: Article[] = [
         { _id: 1, title: "Article 1", text: "Text for article 1", created: new Date() },
         { _id: 2, title: "Article 2", text: "Text for article 2", created: new Date() }
     ];

    // Observable openArticle source
    private _openArticleSource = new ReplaySubject<Article>(1);
    // Observable openArticle stream
    openArticle$ = this._openArticleSource.asObservable();

    // Simulate GET /articles/:_id
    getArticleById(_id: number): Article {
        let article = this.articles
            .filter(article => article._id === _id)
            .pop();

        console.log("Pushing article to observable : ", article) // This gets logged, along with the article
        this._openArticleSource.next(article); // Should trigger the subscription, but doesn't

        return article;
    }
}

Here's the listening component :

import { Component } from '@angular/core';
import { Subscription } from 'rxjs/Subscription';
import { ArticleService } from '../article.service';

@Component({
  selector: 'column-open-article',
  templateUrl: './column-open-article.component.html',
  providers: [ArticleService]
})


export class ColumnOpenArticleComponent {

  openArticle;
  subscription: Subscription;

  constructor(private articleService: ArticleService) {
    this.subscription = articleService
              .openArticle$
              .subscribe(article => {
                console.log("Subscription triggered", article); // Never gets logged
                this.openArticle = article; // Never gets updated
              })
  }


  ngOnDestroy() {
    // prevent memory leak when component is destroyed
    console.log("Unsubscribing")
    this.subscription.unsubscribe();
  }
}

Then I call getArticleById(1) from another component, and I can see "Pushing article to observable" in the console, so the observable is updated but doesn't trigger the subscription.

If I place the subscription directly inside the service, it triggers without problem, and I can see "Subscription triggered" in the console.

But if I place the same code in the component, it doesn't work.

Any ideas?

Community
  • 1
  • 1
Jeremy Thille
  • 26,047
  • 12
  • 43
  • 63

1 Answers1

2

Looks like you have multiple instances of ArticleService.

Don't provide ArticleService on every component because this way every component instance will get a new ArticleService instance.

Either provide it on a common parent component so that both get the same instance from the parent injected

or

provide it in @NgModule{ providers: [ArticleService]}, then it will be provided at the application root scope andevery component and service that injects ArticleService will get the same instance injected.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • Oh so that's how it works! I thought that services were singletons, like in Angular 1. Actually they have to be imported once, at module level, and then not in each of the components. gotcha. It works now, thank you very much! – Jeremy Thille Oct 22 '16 at 18:25
  • They are singleton per provider. If you have more than one provider you'll get more than one instance. `providers: []` of `@NgModule()` are all hoisted to the root scope, therefore multiple such providers still result in a single instance but for providers on components or directives you get a different instance for every component or directive. Lazy loaded modules get their own root scope. – Günter Zöchbauer Oct 22 '16 at 18:27