具有函数return Observable和async pipe的模板绑定 [英] Template binding with function return Observable and async pipe

查看:123
本文介绍了具有函数return Observable和async pipe的模板绑定的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

注意:这是的简化问题带有可观察到的异步管道问题的角度模板绑定

模板:

 <div>{{foo()$ | async}}</div>
 

源代码:

 import { Component } from "@angular/core";
import { BehaviorSubject, of, Observable } from "rxjs";
import { tap, delay, map, switchMap, concatMap } from "rxjs/operators";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  private index = 0;
  foo$(): Observable<any> {
    console.log("aaa")
    return of("Delayed");
  }
}
 

上面的代码按预期工作:

但是,如果我将.pipe(delay(1))添加到foo$():

   foo$(): Observable<any> {
    return of("Delayed").pipe(delay(1));
  }
 

它将无法正常运行,并在控制台日志中保留"aaa".

请参见 https://stackblitz.com/edit/angular-qbhkg3

解决方案

从模板中调用的方法在每个更改检测周期都被调用.因为您正在使用async管道,所以每次发射都会触发更改检测.因此,基本上,您正在创建一个无限循环,这就是为什么它永远不会显示值的原因,因为更改检测永远不会完成:

  1. 在视图初始化模板上调用foo$()
  2. foo$()创建一个-new-可观察到的并延迟发射
  3. 可观察物在延迟后发出
  4. emit触发async管道内的变化检测
  5. 更改检测从模板中调用foo$(),我们回到步骤2

目前尚不清楚您要实现的目标,但是我不认为您应该从要在模板中使用的方法中返回Observables.这些应该是只读类字段:

readonly foo$ = of("Delayed").pipe(delay(1));

但是可以使用一种方法,但是必须确保该方法返回相同的Observable:

private readonly _foo$: Observable<any> = of("Delayed").pipe(delay(1));

foo$(): Observable<any> {
  console.log('here');
  return this._foo$;
}

示例

由于可观察对象保持不变(===),所以在引擎盖上一切都很好.将pipe添加到Observable后,您将创建一个新引用,然后回到无限循环.


如果仅返回of('delayed'),它就不会到达无限循环的原因是,因为Observable并非以这种方式异步. Observable会立即向async管道返回一个值,并且当异步管道调用detectChanges()时,什么都不会发生,因为它仍然与触发foo$()模板调用的更改检测周期处于同一周期.


我看到您还链接了您发布的上一个问题,该问题涉及装饰器的使用.您应该将该装饰器更改为类字段装饰器,然后可以执行以下操作:

@NeedsElement(sp(115621), ap(116215))
readonly insuredType$!: Observable<string>;

我想我可以想到一种使它与方法调用一起工作的方法,但是在我深入探讨该方法之前,我首先想知道为什么首先要使其成为方法调用

Note this is simplified question of Angular template binding with Observable async pipe issue

template:

<div>{{foo()$ | async}}</div>

source code:

import { Component } from "@angular/core";
import { BehaviorSubject, of, Observable } from "rxjs";
import { tap, delay, map, switchMap, concatMap } from "rxjs/operators";

@Component({
  selector: "my-app",
  templateUrl: "./app.component.html",
  styleUrls: ["./app.component.css"]
})
export class AppComponent {
  private index = 0;
  foo$(): Observable<any> {
    console.log("aaa")
    return of("Delayed");
  }
}

The above code works as expected:

However if I added .pipe(delay(1)) to the foo$():

  foo$(): Observable<any> {
    return of("Delayed").pipe(delay(1));
  }

it won't work and keep "aaa" in the console log.

See https://stackblitz.com/edit/angular-qbhkg3

解决方案

A method called from the template, is called every change detection cycle. Because you are using the async pipe, the change detection is triggered with every emit. So basically you are creating an infinite loop and that's why it will never show a value, because the change detection never finishes:

  1. on view init template calls foo$()
  2. foo$() creates a -new- observable and delays the emit
  3. the observable emits after the delay
  4. the emit triggers a change detection from within the async pipe
  5. change detection calls foo$() from the template, and we're back to step 2

It's not really clear what you are trying to achieve, but I don't think you should return Observables from methods to be consumed in templates. These should be readonly class field:

readonly foo$ = of("Delayed").pipe(delay(1));

It is however possible to use a method, but you have to make sure this method returns the same Observable:

private readonly _foo$: Observable<any> = of("Delayed").pipe(delay(1));

foo$(): Observable<any> {
  console.log('here');
  return this._foo$;
}

example

Because the observable object stays the same (===), it's all good in the hood. Once you add a pipe to the Observable you create a new reference and you come back into the infinite loop.


The reason it does not reach the infinite loop if you just return of('delayed'), is because the Observable is not asynchronous this way. The Observable will return a value immediately to the async pipe, and when the async pipe calls detectChanges() nothing really happens, because it's still in the same cycle as the change detection cycle which triggered the foo$() template call.


I see you also linked a previous question you posted which involves the use of a decorator. You should change that decorator to a class field decorator, and you can then do the following:

@NeedsElement(sp(115621), ap(116215))
readonly insuredType$!: Observable<string>;

I think I can think of a way to make it work with a method call, but before I dive into that, I first want to know why you want it to be a method call in the first place

这篇关于具有函数return Observable和async pipe的模板绑定的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆