Using an array from Observable Object with ngFor and Async Pipe Angular 2

TypescriptAngularRxjsObservable

Typescript Problem Overview


I am trying to understand how to use Observables in Angular 2. I have this service:

import {Injectable, EventEmitter, ViewChild} from '@angular/core';
import {Observable} from "rxjs/Observable";
import {Subject} from "rxjs/Subject";
import {BehaviorSubject} from "rxjs/Rx";
import {Availabilities} from './availabilities-interface'

@Injectable()
export class AppointmentChoiceStore {
	public _appointmentChoices: BehaviorSubject<Availabilities> = new BehaviorSubject<Availabilities>({"availabilities": [''], "length": 0})

	constructor() {}

	getAppointments() {
		return this.asObservable(this._appointmentChoices)
	}
	asObservable(subject: Subject<any>) {
		return new Observable(fn => subject.subscribe(fn));
	}
}

This BehaviorSubject is pushed new values as so from another service:

that._appointmentChoiceStore._appointmentChoices.next(parseObject)

I subscribe to it in the form of an observable in the component I want to display it in:

import {Component, OnInit, AfterViewInit} from '@angular/core'
import {AppointmentChoiceStore} from '../shared/appointment-choice-service'
import {Observable} from 'rxjs/Observable'
import {Subject} from 'rxjs/Subject'
import {BehaviorSubject} from "rxjs/Rx";
import {Availabilities} from '../shared/availabilities-interface'


declare const moment: any

@Component({
	selector: 'my-appointment-choice',
	template: require('./appointmentchoice-template.html'),
	styles: [require('./appointmentchoice-style.css')],
	pipes: [CustomPipe]
})

export class AppointmentChoiceComponent implements OnInit, AfterViewInit {
	private _nextFourAppointments: Observable<string[]>
	
	constructor(private _appointmentChoiceStore: AppointmentChoiceStore) {
		this._appointmentChoiceStore.getAppointments().subscribe(function(value) {
			this._nextFourAppointments = value
		})
	}
}

And the attempt to display in the view as so:

  <li *ngFor="#appointment of _nextFourAppointments.availabilities | async">
         <div class="text-left appointment-flex">{{appointment | date: 'EEE' | uppercase}}

However, availabilities isn't yet a property of the observable object so it errors out, even though I define it in the availabilities interface as so:

export interface Availabilities {
  "availabilities": string[],
  "length": number
}

How can I display an array asynchronously from an observable object with the async pipe and *ngFor? The error message I get is:

browser_adapter.js:77 ORIGINAL EXCEPTION: TypeError: Cannot read property 'availabilties' of undefined

Typescript Solutions


Solution 1 - Typescript

Here's an example

// in the service
getVehicles(){
    return Observable.interval(2200).map(i=> [{name: 'car 1'},{name: 'car 2'}])
}

// in the controller
vehicles: Observable<Array<any>>
ngOnInit() {
    this.vehicles = this._vehicleService.getVehicles();
}

// in template
<div *ngFor='let vehicle of vehicles | async'>
    {{vehicle.name}}
</div>

Solution 2 - Typescript

Who ever also stumbles over this post.

I belive is the correct way:

  <div *ngFor="let appointment of (_nextFourAppointments | async).availabilities;"> 
    <div>{{ appointment }}</div>
  </div>

Solution 3 - Typescript

I think what u r looking for is this

<article *ngFor="let news of (news$ | async)?.articles">
<h4 class="head">{{news.title}}</h4>
<div class="desc"> {{news.description}}</div>
<footer>
    {{news.author}}
</footer>

Solution 4 - Typescript

If you don't have an array but you are trying to use your observable like an array even though it's a stream of objects, this won't work natively. I show how to fix this below assuming you only care about adding objects to the observable, not deleting them.

If you are trying to use an observable whose source is of type BehaviorSubject, change it to ReplaySubject then in your component subscribe to it like this:

Component
this.messages$ = this.chatService.messages$.pipe(scan((acc, val) => [...acc, val], []));
Html
<div class="message-list" *ngFor="let item of messages$ | async">

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionC. KearnsView Question on Stackoverflow
Solution 1 - TypescriptRelu MesarosView Answer on Stackoverflow
Solution 2 - TypescriptMartin SøbergView Answer on Stackoverflow
Solution 3 - TypescriptNixon MathewView Answer on Stackoverflow
Solution 4 - TypescriptPost ImpaticaView Answer on Stackoverflow