Usando una matriz de Observable Object con ngFor y Async Pipe Angular 2

93

Estoy tratando de entender cómo usar Observables en Angular 2. Tengo este servicio:

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));
    }
}

A este BehaviorSubject se le envían nuevos valores desde otro servicio:

that._appointmentChoiceStore._appointmentChoices.next(parseObject)

Me suscribo a él en forma de observable en el componente en el que quiero mostrarlo:

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
        })
    }
}

Y el intento de mostrar en la vista así:

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

Sin embargo, la disponibilidad aún no es una propiedad del objeto observable, por lo que se produce un error, incluso aunque lo defina en la interfaz de disponibilidades de la siguiente manera:

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

¿Cómo puedo mostrar una matriz de forma asíncrona desde un objeto observable con la tubería asíncrona y * ngFor? El mensaje de error que recibo es:

browser_adapter.js:77 ORIGINAL EXCEPTION: TypeError: Cannot read property 'availabilties' of undefined
C. Kearns
fuente
¿Cuál es el mensaje de error real?
Günter Zöchbauer
editado para agregar error
C. Kearns
con el último angular-rc1, la sintaxis es*ngFor="let appointment of _nextFourAppointments.availabilities | async">
Jagannath
esto es cierto, pero no está causando el error. simplemente lanza una advertencia.
C.Kearns
3
Creo que hay un error tipográfico en alguna parte. El error dice availabiltiesmientras debería haberavailabilities
Ivan Sivak

Respuestas:

154

Aquí hay un ejemplo

// 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>
Relu Mesaros
fuente
Sin embargo public _appointmentChoices: Subject<any> = new Subject() getAppointments() { return this._appointmentChoices.map(object=>object.availabilities).subscribe() } , mi función get está devolviendo un sujeto:, en el controlador cuando lo configuro igual, obtengo el error:, browser_adapter.js:77Error: Invalid argument '[object Object]' for pipe 'AsyncPipe'¿cómo convierto el sujeto en un observable?
C.Kearns
public _appointmentChoices: Subject<any> = new Subject() getAppointments() { return (this._appointmentChoices.map(object=>object.availabilities).asObservable()) } } esto me da el error:, property asObservable does not exist on type observablepero _appointmentChoices es un Subject?
C.Kearns
¡Ya era un observable! ¡Solo necesitaba suscribirme!
C.Kearns
Tuve un problema adicional con la integración de materias. Aquí hay un StackBlitz usando observables y sujetos: stackblitz.com/edit/subject-as-observable-list-example
IceWarrior353
12

Quien alguna vez también se tropiece con esta publicación.

Creo que es la forma correcta:

  <div *ngFor="let appointment of (_nextFourAppointments | async).availabilities;"> 
    <div>{{ appointment }}</div>
  </div>
Martin Søberg
fuente
1

Creo que lo que buscas es esto

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

Nixon Mathew
fuente
1

Si no tiene una matriz pero está tratando de usar su observable como una matriz, aunque sea una secuencia de objetos, esto no funcionará de forma nativa. A continuación, muestro cómo solucionar este problema, asumiendo que solo le importa agregar objetos al observable, no eliminarlos.

Si está tratando de usar un observable cuya fuente es de tipo BehaviorSubject, cámbielo a ReplaySubject y luego en su componente suscríbase a él de esta manera:

Componente

this.messages$ = this.chatService.messages$.pipe(scan((acc, val) => [...acc, val], []));

HTML

<div class="message-list" *ngFor="let item of messages$ | async">
Post Impatica
fuente
En lugar del scanoperador puede utilizar.pipe(toArray())
MotKohn
Otro error al crear el tuyo propio Subjectes no llamar complete(). El acumulador nunca funcionará.
MotKohn