【问题标题】:ngFor cannot find a differ supporting object ... using BehaviorSubjectngFor 找不到不同的支持对象...使用 BehaviorSubject
【发布时间】:2018-05-20 15:47:56
【问题描述】:

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

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

对我做错了什么有什么想法吗?除了一切。 :D 哈哈

这是我的服务

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>  

【问题讨论】:

  • 看起来非常快,我认为这是因为您的服务是异步的,因此当您的数据 isLoading 为 false 时,它​​会尝试显示并遍历您的数组。但是在那一刻,您的数组没有设置,因为它是异步的。尝试从*ngIf=!isLoading 更新到*ngIf=!isLoading &amp;&amp; heroes &gt; 0
  • this.dataSubject.next(data); 替换为console.log(data); this.dataSubject.next(data);。控制台记录了什么?真的是数组吗?
  • @JBNizet 是的,数据是一个包含 10 个元素的数组。
  • 证明它。发布在控制台中记录的结果。如果更容易复制和粘贴,请使用console.log(JSON.stringify(data))
  • @JBNizet {"data":[{"id":11,"name":"蝙蝠侠"},{"id":12,"name":"超人"},{" id":13,"name":"蜘蛛侠"},{"id":14,"name":"雷神"},{"id":15,"name":"金刚狼"},{"id" :16,"name":"神奇女侠"},{"id":17,"name":"美国队长"},{"id":18,"name":"钢铁侠"},{"id ":19,"name":"绿巨人"},{"id":20,"name":"Duke"}]}

标签: angular angular2-services


【解决方案1】:

与其定义一个返回可观察对象的函数,您不应该定义一个实际上是可观察对象的属性吗?例如:

服务

public heroDataStream: Observable&lt;Hero[]&gt; = this.dataSubject.asObservable();

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

您可能会遇到冷与热的问题,因为在调用订阅之前不会创建 Observable,因为它的定义在函数中。换句话说,它很冷。你希望它的声明立即发生,这意味着它需要很热并且准备好了。因此,将其定义为服务中的属性会在服务中声明它。我可能是错的,当谈到 RxJs 以及一切如何被卷起时,这绝对是在触及我知识深度的界限。

【讨论】:

  • 我不确定这是否重要,尽管我对 observable 的了解还不是很清楚。我的理解是在调用订阅之前它们是“冷的”。关于属性 vs 方法,个人风格而已。
  • 我怀疑您的 heros 正在获得分配给它的 observable,而不是您想要的数据。您应该通过控制台将其注销并查看实际得到的结果。
【解决方案2】:

呃,我讨厌回答自己的问题,但这是一个很大的“d'oh”。

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

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

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

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

解决了这个问题。

非常感谢 JB Nizet 为我指明了正确的方向!

【讨论】:

    猜你喜欢
    • 2020-08-29
    • 2019-09-15
    • 2019-09-08
    • 2021-03-17
    • 1970-01-01
    • 1970-01-01
    • 2022-11-18
    • 2022-11-19
    • 2018-10-12
    相关资源
    最近更新 更多