【问题标题】:Angular - Observable with async pipe used multiple times in template... Good Practice or Bad?Angular - 在模板中多次使用异步管道时可观察到......好的做法还是坏的?
【发布时间】:2019-03-15 22:06:12
【问题描述】:

如果我需要在我的组件模板中绑定来自同一个可观察对象的多个属性...

例如:

<my-random-component[id]="(myObservable$ | async).id">
...
<my-random-component2[name]="(myObservable$ | async).name">

...我最好像上面那样做(我看到很多),还是在我的 .ts 文件中订阅我的 observable 更有效,设置单个对象变量,然后绑定到那?后一种方法的想法是 observable 只会被调用一次。

问题:

  1. 上述代码中的 observable 是否在每次通过 | 使用时都会被调用?异步?
  2. 即使在我的模板中使用了 10 次,编译器是否会在后台执行任何效率魔法以仅调用可观察对象一次?
  3. 哪种方法更好/首选?

谢谢!

【问题讨论】:

  • 这可能会基于意见关闭,但我也很好奇 :)
  • 不幸...我真的认为这是一个好问题。它们要么相同,要么效率更高(我原以为)。

标签: angular asynchronous pipe components observable


【解决方案1】:

另一种方法是将结构和内容呈现职责分开,而不受*ngIf的约束。

您可以将ng-templateng-container 与上下文一起使用:

<ng-template #userTemplate let-user>
  <user-address [zipCode]="user?.zipCode"></user-address>
  <user-car [carCode]="user?.carCode"></user-car>
</ng-template>

<ng-container
  *ngTemplateOutlet="
    userTemplate;
    context: { $implicit: user$ | async }
  "
>
</ng-container>

【讨论】:

    【解决方案2】:

    使用异步管道可以更轻松地处理订阅。与组件中的订阅不同,它会自动处理退订。

    也就是说,有一个比示例显示的更好的模式。您可以用两种不同的方式编写它,而不是对组件进行多次异步调用。我假设这些组件在同一个模板文件中:

        <div *ngIf="(myObservable$ | async) as myObservable">
          <my-random-component [id]="myObservable.id">
          <my-random-component2 [name]="myObservable.name">
        </div>
    

    将代码包装在ngIf 中会做两件事:

    • 减少重复代码
    • myObservable$ 准备好之前,组件不存在

    如果您想坚持每次都调用 async,还有一个想法:

        // COMPONENT
        name$: Observable<string>;
        id$: Observable<string>;
        
        ngOnInit() {
            // Return the exact value you want rather than the full object
        
            this.name$ = OBSERVABLE_SOURCE
            .pipe(
                map(res => res.name)
            );
        
            this.id$ = OBSERVABLE_SOURCE
            .pipe(
                map(res => res.id)
            );
        }
    
        // TEMPLATE
        <my-random-component [id]="(id$ | async)">
        <my-random-component2 [name]="(name$ | async)">
    

    没有订阅,管道不会自动运行。你可以用它来映射、点击或做任何你想做的事情,在你添加async/.subscribe()之前它不会运行。

    【讨论】:

    • 这些是一些好主意,感谢您抽出宝贵时间回复。我没有想到第一个选项。我仍然想知道简单地订阅是否是更好的方法。我真的不介意 sub/unsub '模式'。
    • 在第二种方法中,API 会被制作 2 次还是一次?
    【解决方案3】:

    如果您有多个可观察对象,您可以将整个页面包装在一个 div 中,该 div 将所有可观察对象收集到一个数据对象中,然后根据需要使用它们:

    <div *ngIf="{
      observable1: myObservable1$ | async,
      observable2: myObservable2$ | async
    } as data">
      ... page content
      {{data.observable1.id}}: {{data.observable1.name}}
    
      {{data.observable2.status}}
    
    </div>
    

    注意:*ngIf="{ ... }" 始终为真。

    功劳归于: https://medium.com/@ofirrifo/extract-multiple-observables-with-the-async-pipe-in-angular-b119d22f8e05

    【讨论】:

    • 我发现即使从一个 observable 开始,这也是一个很好的方法,因为你往往会在不知不觉中得到多个 observables,然后你不需要重构第一个 observable 使用
    【解决方案4】:

    您可以使用share() 来使用相同的可观察对象并从html 中多次调用它。像这样:

    this.myObservable$ = this.anotherObservable$.pipe(share());
    

    那么无论你从 HTML 中调用 observable 多少次,它都只会被调用一次。

    【讨论】:

    • 我使用sharedReplay() 管理它工作。不要问我为什么?
    • 我们的代码尝试使用这种方法,我们使用相同的 observable 向下传递给多个子组件。尽管使用了.pipe(share());,但似乎 HTML 中的第一个组件是唯一接收数据的组件。可能是我们用错了。 :) 我们刚刚将异步管道重构为 &lt;ng-container&gt; 父级,因此我们只订阅了一次。
    • @AlexandreAnnic shareReplay() 准确地说。也适合我。
    猜你喜欢
    • 2018-11-19
    • 2017-01-10
    • 1970-01-01
    • 2019-06-27
    • 1970-01-01
    • 1970-01-01
    • 2020-02-01
    • 2020-07-27
    • 1970-01-01
    相关资源
    最近更新 更多