2017-12-06 3 views
0

Zurück zum Lernen Angular und das Tour of Heroes Tutorial erweitern. Ich habe einen gemeinsamen Datendienst, der Daten in ein BehaviorSubject lädt. Das Problem ist, dass wenn ich versuche, die Daten mit * ngFor zu iterieren, bekomme ich den Fehler "kann ein anderes unterstützendes Objekt nicht finden". Von all den anderen Fragen, ich verstehe, dass es versucht, an ein Objekt und nicht an ein Array zu binden, aber für das Leben von mir kann ich nicht herausfinden, warum. Oder welches Objekt muss in ein Array konvertiert werden?ngFor kann kein anderes unterstützendes Objekt finden ... mit BehaviorSubject

Ich benutze Angular 5.0.5. Interessanterweise funktionierte das mit Angular 4, aber anscheinend habe ich etwas beim Upgrade kaputt gemacht.

Irgendwelche Gedanken darüber, was ich falsch gemacht habe? Neben allem. : D lol

Das ist mein Service

import { Injectable, EventEmitter } from '@angular/core'; 
import { HttpHeaders, HttpClient } from '@angular/common/http'; 

import { Subject } from 'rxjs/Subject'; 
import { BehaviorSubject } from 'rxjs/BehaviorSubject'; 
import { Observable } from 'rxjs/Observable'; 

import { Hero } from '../models/hero'; 

@Injectable() 
export class HeroService { 

    private heroesUrl = 'api/heroes'; 
    private headers = new HttpHeaders({'Content-Type': 'application/json'}); 

    private loadingSubject = new BehaviorSubject<boolean>(false); 
    private dataSubject = new BehaviorSubject<Hero[]>([]); 

    private http: HttpClient; 

    constructor(http: HttpClient) { 
     this.http = http; 
    } 

    public getLoadingStream(): Observable<boolean> { 
     return this.loadingSubject.asObservable(); 
    } 
    public getDataStream(): Observable<Hero[]> { 
     return this.dataSubject.asObservable(); 
    } 

    public load(): void { 
     this.loadingSubject.next(true); 
     this.http.get<Hero[]>(this.heroesUrl).subscribe(data => { 
      this.dataSubject.next(data); 
      this.loadingSubject.next(false); 
     }); 
    } 

    public search(term: string): void { 
     this.loadingSubject.next(true); 
     this.http 
      .get<Hero[]>(`${this.heroesUrl}/?name=${term}`) 
      .subscribe(data => { 
       this.dataSubject.next(data); 
       this.loadingSubject.next(false); 
     }); 
    } 
} 

Liste Komponente

import { Component, OnInit, OnDestroy, Input, Output, EventEmitter } from '@angular/core'; 
import { Observable } from 'rxjs/Observable'; 
import { Subscription } from 'rxjs/Subscription'; 

import { HeroService } from '../../services/hero.service'; 
import { Hero } from '../../models/hero'; 

@Component({ 
    selector: 'hero-list', 
    templateUrl: './hero-list.component.html', 
    styleUrls: ['./hero-list.component.css'] 
}) 
export class HeroListComponent implements OnInit { 

    private dataSubscription: Subscription; 
    private loadingSubscription: Subscription; 

    private heroService: HeroService; 

    heroes: Hero[] = []; 
    isLoading: boolean; 

    @Output() 
    public onHeroSelected = new EventEmitter<Hero>(); 

    constructor(heroService: HeroService) { 
    this.heroService = heroService; 
    } 

    ngOnInit() { 
    this.loadingSubscription = this.heroService.getLoadingStream() 
     .subscribe(loading => { 
      this.isLoading = loading; 
     }); 

    this.dataSubscription = this.heroService.getDataStream() 
     .subscribe(data => { 
      this.heroes = data; 
     }); 
    } 

    ngOnDestory() { 
    this.dataSubscription.unsubscribe(); 
    this.loadingSubscription.unsubscribe(); 
    } 

    onSelect(hero: Hero) { 
    this.onHeroSelected.emit(hero); 
    } 
} 

Liste Vorlage

<div *ngIf="isLoading">Loading ...</div> 

<div *ngIf="!isLoading" class="ui relaxed divided list"> 
    <div *ngFor="let hero of heroes" class="item" (click)="onSelect(hero)"> 
    <span class="ui blue circular label">{{ hero.id }}</span> 
    <div class="content"> 
     <a class="header">{{ hero.name }}</a> 
     <div class="description">...</div> 
    </div> 
    </div> 
</div> 
+0

sieht wirklich schnell aus, ich denke es ist, weil Ihr Dienst async ist, wenn Ihre Daten 'isLoading' also falsch ist, wird es versuchen, Ihr Array anzuzeigen und zu iterieren. Aber in diesem Moment ist Ihr Array nicht gesetzt, weil es asynchron ist. versuche von '* ngIf =! isLoading' nach '* ngIf =!isLoading && heroes> 0' – sheplu

+0

Ersetzen Sie 'this.dataSubject.next (data);' durch 'console.log (data); this.dataSubject.next (Daten); '. Was wird auf der Konsole protokolliert? Ist es wirklich ein Array? –

+0

@JBNizet ja, die Daten sind ein Array mit 10 Elementen. –

Antwort

0

Anstatt eine Funktion definiert, die eine beobachtbare zurückgibt, sollten Sie nicht eine Eigenschaft definieren, die tatsächlich beobachtbar ist? Zum Beispiel:

Dienst

public heroDataStream: Observable<Hero[]> = this.dataSubject.asObservable();

Dann würden Sie direkt an heroDataStream abonnieren. Das ist in der Regel, wie ich BehaviorSubjects in Observables umwandeln, anstatt sie in eine neue Funktion einzubinden.

Möglicherweise tritt ein Kalt-vs-Problem auf, bei dem das Observable erst erstellt wird, wenn das Abonnement aufgerufen wird, da seine Definition innerhalb der Funktion liegt. Mit anderen Worten, es ist kalt. Sie möchten, dass die Deklaration sofort stattfindet und heiß sein muss. Wenn Sie es als eine Eigenschaft in Ihrem Service definieren, wird dies mit dem Service erklärt. Ich könnte mich irren, das ist definitiv die Grenze meiner Tiefe des Wissens, wenn es um RxJs geht und wie alles aufgespult wird.

+0

Ich bin mir nicht sicher, ob es wichtig ist, obwohl ich mich noch nicht wirklich gut beobachten kann. Es ist mein Verständnis, dass sie "kalt" sind, bis sie abonnieren. In Bezug auf die Eigenschaft vs Methode, es ist nur ein persönlicher Stil. –

+0

Ich vermute, dass Ihre 'Helden' eine beobachtbare zugewiesen bekommen, anstatt die Daten, die Sie wollen. Sie sollten die Konsole abmelden und sehen, was Sie tatsächlich bekommen. – joshrathke

0

Ugh, ich hasse es, meine eigenen Fragen zu beantworten, aber das war ein großer "d'oh".

Die Ursache des Problems lag darin, dass ich eine In-Memory-Web-API verwendet habe und nicht erkannte, dass es die Antwort in einem Datenobjekt kapselt.

Also die Fehlermeldung war richtig, es wurde versucht, über ein Objekt {Daten: []} anstelle eines Arrays zu iterieren.

Aktualisieren der In-Memory-Web-API und in der Datenkapselung

HttpClientInMemoryWebApiModule.forRoot(InMemoryDataService, {dataEncapsulation: false}) 

löste die Ausgabe falsch setzen.

Vielen Dank an JB Nizet dafür, dass er mich in die richtige Richtung gelenkt hat!

Verwandte Themen