Async Pipe Fails to Render Observable

2019-07-17 03:17发布

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?

2条回答
唯我独甜
2楼-- · 2019-07-17 03:19

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.

查看更多
我只想做你的唯一
3楼-- · 2019-07-17 03:43

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); // <----
}
查看更多
登录 后发表回答