ngFor使用BehaviorSubject无法找到不同的支持对象

时间:2017-12-06 17:07:09

标签: angular angular2-services

回到学习Angular并扩展英雄之旅教程。我有一个共享数据服务,可以将数据加载到BehaviorSubject中。问题是,当我尝试使用* ngFor迭代数据时,我得到“找不到不同的支持对象”错误。从所有其他问题,我得到它试图绑定到一个对象而不是一个数组,但对于我的生活,我无法弄清楚为什么。或者需要将哪个对象转换为数组。

我正在使用Angular 5.0.5。有趣的是,这与Angular 4有关,但显然我在升级中已经破坏了一些东西。

对我做错了什么的想法?除了一切。 :D lol

这是我的服务

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

列出组件

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

列出模板

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

2 个答案:

答案 0 :(得分:0)

不是定义一个返回一个observable的函数,你不应该定义一个实际上是可观察的属性吗?例如:

<强>服务

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

然后您将直接订阅heroDataStream。这通常是我如何将BehaviorSubjects转换为observables而不是将它们包装在一个新函数中。

您可能遇到了一个冷和热的问题,在调用订阅之前,Observable没有被创建,因为它的定义在函数内。换句话说它很冷。你希望它的声明立即发生,这意味着它需要很热并且准备好了。因此,将其定义为服务中的属性会使用该服务声明它。我可能是错的,当谈到RxJs以及如何将所有内容都假装起来时,这肯定会让我深入了解我的知识深度。

答案 1 :(得分:0)

呃,我讨厌回答我自己的问题,但这是一个很大的&#34; d&#39;哦&#34;。

问题的根本原因是我使用的是In-Memory-Web-API,并没有意识到它正在将响应封装在数据对象中。

因此错误消息是正确的,它试图迭代对象{data:[]}而不是数组。

升级In-Memory-Web-API并将false放入数据封装

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

解决了这个问题。

非常感谢JB Nizet指出我正确的方向!