使用模板中的异步管道进行观察不适用于单个值

时间:2016-09-02 16:24:03

标签: angular rxjs rxjs5

我有一个组件,它向我的服务请求一个Observable对象(也就是说,底层的http.get返回一个单独的对象)。

对象(可观察对象)与模板中的异步管道一起使用。 不幸的是我收到了一个错误:

Cannot read property 'lastname' of null

我一直在打破这个问题。类似的代码类型在对象列表上正确工作(与* ngFor一起使用)。

 <li>{{(person | async)?.lastname}}</li>

我服务中的方法:

  ....
  getPerson(id: string): Observable<Person> {        
     let url = this.personsUrl + "/" + id;
     return this.http.get(url, {headers: new Headers({'Accept':'application/json'})})
                .map(r => r.json())
                .catch(this.handleError); //error handler
  }
  ....

在我的组件中:

//... imports ommitted

@Component({
  moduleId: module.id,
  selector: 'app-details-person',
  templateUrl: 'details-person.component.html',
  styleUrls: ['details-person.component.css'],
})
export class DetailsPersonComponent implements OnInit, OnDestroy {

   person: Observable<Person>;
   sub: Subscription;

   constructor(private personService: PersonService, private route: ActivatedRoute) {
   }

  ngOnInit() {
     this.sub = this.route.params.subscribe(params => {
                 let persId = params['id'];
                 this.person = this.personService.getPerson(persId);
                });
  }

  ngOnDestroy(): void {
     this.sub.unsubscribe();
  }
}

显然,observable是/在管道中返回一个空对象值。 我已经检查过我是否真的从我的服务中获得了一个非空的Observable,我可以确认我的服务中存在一个(底层)对象。

当然,我可以在从服务中检索Observable后订阅我的组件中的observable,但我真的想使用异步构造。

顺便问一下,其他问题:是否已有一种模式如何处理异步管道中发生的错误? (使用异步管道的缺点......错误会延迟到视图的渲染时间。

4 个答案:

答案 0 :(得分:4)

视图第一次呈现时,person未定义,因为只有当params订阅触发时,才会异步创建该组件属性。您需要最初创建一个空的observable。而不是

person:Observable<Person>;

person = Observable.of<Person>(Person());  // or however you create an empty person object
    // the empty object probably needs a lastname field set to '' or null

我通常通过

处理服务中的错误
  1. 返回一个空的可观察
  2. 将错误报告给某些应用范围(单例)服务。然后,某些(单例)组件会在页面的某处显示这些错误(如果适用)。

答案 1 :(得分:2)

请尝试更改

person:Observable< Person>

person:Observable< any>;

如果这不起作用,请打印此{{ person | async | json }},当然请更改上面的内容,看看你得到了什么。

答案 2 :(得分:1)

如果您使用Subject<T>而不是ReplaySubject<T>BehaviorSubject<T>的话,就会发生这种情况。

Subject<T>发生的事情是,它一次仅向当时注册的所有听众“广播”其值。

因此,如果您在可观察的next('hello word')上调用message: Subject<string>,则只有在该“ UI”当时存在时,它才能更新UI。

因此,在此示例中,消息不会出现-除非您两次致电next

<div *ngIf="message | async">
   here is the message {{ message | async }}
</div>

最简单的解决方案是使用ReplaySubject<string>(1),其中1是它记住的值的数量(我们在这里只需要一个)。

答案 3 :(得分:0)

您无需订阅ngOnInit中可观察到的路线数据。

您不必每次发送可观察路线数据时都订阅和创建全新的可观察路线,而是可以将路线数据与personService可观察路线链接在一起,形成一个新的可观察路线。这是函数式编程的本质:

  

函数式反应式编程的本质是指定   一个值在声明时完全是动态的。

请记住,您可以在声明时声明有关“人”的所有内容:

person: Observable<Person> = this.route.params.pipe(
  map(params => params.id),
  mergeMap(persId => this.personService.getPerson(persId)),
);

这有很多好处:

  • 可观察对象直到有人订阅它(例如异步管道)时才真正触发。这意味着除非您在模板中使用异步管道,否则它将不会请求此人。
  • 您不需要处理退订。异步管道将为您做到这一点。
  • 通过查看代码可以很容易地看出“ Person”的构成。全部定义在一个地方。
  • 您不需要进行可观察到的空初始化,这可能不是有效的“人”。

话虽如此,异步管道仍可以默认为null,您可以在模板中使用ngIf对其进行处理:

 <li *ngIf="(person | async) as person">{{person.lastname}}</li>