3

e(1): Updated code to address typo's in original question.
e(2): Attempted to 'JSON.stringify()' the data pushed into my JSON object in 'createContent()'. No luck.
e(3): Added plnkr as requested: http://plnkr.co/edit/j024MYCQbypAGaf762fT

I have a model...

class Content {
  constructor(public name: string) { }
}

I have a service...

class ContentService {
  private _contents$: Subject<Content[]>
    = new Subject<Content[]>();

  get contents$(): Observable<Content[]> {
    return this._contents$.asObservable();
  }

  private _data: { contents: Content[] } = { contents: [] };

...

  load(): void {
    createContent(new Content("Test Content..."));
  }

  createContent(item: Content): void {
    this._data.contents.push(item);
    this._contents$.next(this._data.contents);
  }
}

I have a component...

@Component({
...
template: `
<h1 *ngFor="let item of contents$ | async">{{ item.name }}</h1>
`
})
class ContentListComponent {
  contents$: Observable<Content[]>;

  constructor(private _contentService: ContentService) { }

  ngOnInit(): void {
    this.contents$ = this._contentService.contents$;
    this._contentService.load();
  }
}

Simply put, I don't understand why the Observable in my component is not functioning correctly.

If I change these around, such that I have an array in the component, subscribe to the service 'contents$' to populate the array, then it's no biggie.

When I log the output of the service and functions called, everything is also dandy. The only symptom I am experiencing is that the "*ngFor" does not appear to be properly handling the stream. It's something obvious, isn't it?

  • I think this is the problem: `this.contents$ = this._contentService.contents$;`. Please try this in your template: `*ngFor="let item of _contentService.contents$ | async"` – Maximilian Riegler Jul 19 '16 at 16:56
  • Can you please try to move this line ` this.contents$ = this._contentService.contents$;` into the contructor? I haven't tried it but maybe the view doesn't like if `contents$` is `null` when it first tries to resolve the bindings. – Günter Zöchbauer Jul 19 '16 at 16:56
  • @rinukkusu: The component's backing object has a property named 'contents$' as well. That was my mistake naming these similarly for this question. I did attempt it however, sourcing the service directly. Nothing, unfortunately. – Dustin Holtz Jul 19 '16 at 16:59
  • @GünterZöchbauer: Nice thought. I moved it to the constructor, but the output is the same. Still no dice. – Dustin Holtz Jul 19 '16 at 17:00
  • A Plunker would be helpful – Günter Zöchbauer Jul 19 '16 at 17:09
  • @GünterZöchbauer: I have had terrible luck getting Plunkr's to work for me from scratch. Let me give it a whack though. – Dustin Holtz Jul 19 '16 at 17:11
  • The `New` button in the Plunker editor provides a nice Angular 2.0 TS template for an easy start. The "live example" link at https://angular.io/docs/ts/latest/quickstart.html (below "Try it!") is slightly different but also a nice template to start with. – Günter Zöchbauer Jul 19 '16 at 17:13
  • @GünterZöchbauer: I have added the _plnkr_ to the original question. Let me know if you are able to view it as it is my first one to share. – Dustin Holtz Jul 19 '16 at 17:38
  • My next thought regards whether this is a topic of hot vs cold observables. I sort of understand them, but I have not experienced handling them in too broad of a stroke. – Dustin Holtz Jul 19 '16 at 17:40
  • Just saw your comment. Your suspicion is correct. See my answer. The Plunker was perfect btw :) – Günter Zöchbauer Jul 19 '16 at 17:48
  • AHHHHH!! BEHAVIOR SUBJECT!! I even looked at this beast and wrote it off because I could not get a clear answer on when to use it. I knew it was something silly. Almost 4 hours of banging my head on the wall why this wouldn't fire off, haha. Thanks! – Dustin Holtz Jul 19 '16 at 17:54
  • Give me a moment to figure out how to mark your answer as the correct one. I have only lurked around Stacked, never submitted, heh. – Dustin Holtz Jul 19 '16 at 17:56
  • When using the Finnish notation, the dollar sign is read as a "s". So, it should be content$, not contents$. – Siim Veskilt Oct 07 '19 at 13:25

2 Answers2

4

The problem is that Angular evaluates the

<h1 *ngFor="let item of contents$ | async">{{ item.name }}</h1>

only when change detection runs the next time.

Because

this._service.load();

is executed sync, the event is emitted before Angular had a chance to subscribe.

If you for example use a BehaviorSubject instead of Subject which emits that last emitted value to new subscribers, then you get the desired behavior.

Plunker example

You could also invoke change detection manually before calling this._service.load(); or call this._service.load(); delayed
but that depends on your specific requirements.

Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
  • 1
    Awesome! Thanks for this - I had no idea why BehaviorSubject was so important until now; just that I had a feeling it was a topic of hot vs cold. Thanks again! – Dustin Holtz Jul 19 '16 at 17:57
0

I see a potential problem in your code:

createContent(item: Content): void {
  this._data.contents$.push(item);
  this._contents$.next(this._data.contents$);
}

I would use the following instead:

createContent(item: Content): void {
  this._data.contents.push(item); // <----
  this._contents$.next(this._data.contents); // <----
}
Thierry Templier
  • 198,364
  • 44
  • 396
  • 360
  • Too many "contents/$", heh. Apologies though, this was just a typo in the original question. I have updated it to reflect the code I have. – Dustin Holtz Jul 19 '16 at 17:03