【问题标题】:Angular: How to pipe Observable before subscribing in templateAngular:如何在订阅模板之前通过管道传输 Observable
【发布时间】:2021-03-10 23:08:30
【问题描述】:

免责声明:这与this问题密切相关。

目前我有两个组件,ParentChildParent 将 Observable 传递给 ChildChild 在模板中订阅此 Observable 以避免生命周期/内存泄漏问题。但是我必须在Child 组件中pipe Observable 来设置一些东西。

父.ts:

public $obs = Observable<any>;
....
loadData() {
   this.obs$ = this.myService.getData();
}

父.html:

<child [data$]="obs$"></child>

child.ts:

@Input() data$ : Observable<any>;

ngOnChanges(changes) {

  if(changes.data$) {
    console.log("changes detected");
    this.data$.pipe(tap(data => console.log("tap worked")));
  }
}

child.html

<div *ngIf="data$ | async as data;">
{{ data || json }}
</div>

数据存在于模板中,控制台日志显示已检测到更改,但从未显示“点击工作”,这让我怀疑我对pipe 的调用可能为时已晚。 在模板“调用”订阅之前有什么方法可以管道?

这个想法是有许多类似于 child 的组件,都在数据对象上做不同的事情。我想过订阅Parent 中的服务并将json 值直接传递给所有孩子,但这需要我在所有孩子中写一个*ngIf

【问题讨论】:

  • 这听起来令人困惑,但你可以编写一个pipe Angular 管道,它以 rxjs pipe()(来自 rxjs 入口点的静态管道)作为其参数,并且与异步管道类似处理输入 observable 何时发生变化。然后像 data$ | pipe: yourPipes | asyncyourPipes = pipe(tap(...)) 一样使用它
  • 至于你当前的尝试,操作符总是返回一个新的 observable,它们不会改变原来的 observable。此外,我实际上并不推荐我上面的方法,但它应该有效。我可能不希望将 observables 作为输入传递,因为这通常需要子组件中的假设,例如关于它被重放。
  • 会不会是您声明了一个名为$obs 的变量,并将this.myService.getData() 的输出分配给this.obs$?还是只是帖子中的错字?
  • @R.Richards 不,这不是错字,这就是我正在做的。有错吗?
  • @IngoBürk 感谢您的建议。这听起来……有点过于复杂了。现在我将plain 数据传递给孩子们。

标签: angular rxjs observable


【解决方案1】:

我建议您不要直接订阅data$。而是创建另一个您可以阅读的 Observable。

@Input() data$: Observable<any>;
customData$: Observable<any>;

ngOnChanges(changes) {
  if(changes.data$) {
    console.log("changes detected");
    this.customData$ = this.data$.pipe(tap(data => console.log("tap worked")));
  }
}
<div *ngIf="customData$ | async as data;">
  {{ data || json }}
</div>

【讨论】:

  • 假设我有多个子组件...这仍然会导致每个组件都有一个网络请求,因为它们都是单独订阅的,对吧?
  • 是的,但您可以使用 shareReplay(1) 管道原始 observable。 shareReplay(1) 他们不会。
  • 你有什么例子吗?使用它而不是“直接”传递数据有什么明显的优点/缺点吗?还是归结为个人喜好?
  • 我想说的是个人喜好。但是一个好的做法是愚蠢的组件,听起来你的子组件是愚蠢的。 medium.com/@jtomaszewski/…
【解决方案2】:

Observables 天生就是懒惰的。在您订阅它们之前,它们不会被执行。要查看“点击有效”,您需要订阅它。

this.data$.pipe(tap(data => console.log("tap worked"))).subscribe();

async 默认为您订阅和取消订阅 observable,还要注意 observable 中的每个订阅者都会获得一个新的执行上下文,并且可以覆盖此行为。

要缓存结果以防订阅延迟,您可以在服务中使用BehaviourSubjectReplaySubject

【讨论】:

  • 这(在某种程度上)解释了为什么不执行点击,但实际上并没有回答问题。
  • 好吧,但我正在订阅,通过模板中的异步。我也在考虑将 BehaviourSubject.asObservable() 传递给孩子并订阅那些,但这仍然不能解决管道“为时已晚”的问题。
【解决方案3】:

我认为问题出在您的 ngOnChanges 中。你有条件

  if(changes.data$) {
    console.log("changes detected");
    this.data$.pipe(tap(data => console.log("tap worked")));
  }

这将检查 data$ 的引用是否会改变,但不会检查 data$ 是否发出您真正想要的新值。因此,您的点击不会被执行。

你可以

 public data$: Observable<any>;
  @Input() set data(data$: Observable<any>) {
    this.data$ = data$.pipe(tap(x => console.log("tap worked")));
  }

【讨论】:

  • 这...会工作吗?我从来没有见过这样的事情。你对此有什么参考吗?
  • 当然。给你:stackblitz.com/edit/…。在匆忙中,我在回答中犯了一个错误。这我当然会纠正
猜你喜欢
  • 1970-01-01
  • 2020-11-13
  • 1970-01-01
  • 1970-01-01
  • 2018-03-13
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 2019-08-16
相关资源
最近更新 更多