【问题标题】:Create directive with data transform使用数据转换创建指令
【发布时间】:2020-06-14 15:44:30
【问题描述】:

我想创建一个 LoaderDirective,它可以获取和观察,加载数据同时显示一个微调器,当它加载时,它将微调器更改为原始数据,同时使用“as”关键字公开加载的数据。

我想这样使用它:<div *load="items$ as items">

这是我目前所拥有的:

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: '[load]',
})
export class LoadDirective<T> {
  private data: T;

  @Input()
  set load(observable: Observable<T>) {
    observable.subscribe({
      next: value => this.loaded(value),
      error: error => this.showError(error)
    });
  }

  constructor(
    private templateRef: TemplateRef<any>,
    private viewContainer: ViewContainerRef,
    private componentFactoryResolver: ComponentFactoryResolver
  ) {
    this.viewContainer.clear();
    this.viewContainer.createComponent(this.componentFactoryResolver.resolveComponentFactory(SpinnerComponent));
  }

  private loaded(value: T) {
    this.data = value;
    this.viewContainer.clear();
    this.viewContainer.createEmbeddedView(this.templateRef, {load: this.data});
  }

  private showError(e: string) {
    this.viewContainer.clear();
    const errorComponent = this.viewContainer.createComponent(this.componentFactoryResolver.resolveComponentFactory(ErrorComponent));
    errorComponent.instance.error = e;
  }
}

问题是输入数据是可观察的,所以它期望输出是可观察的。有没有比管道上的TransformPipe 类似的方法来改变它?我该如何解决这个问题?

【问题讨论】:

  • 我可以使用 async pip 来做到这一点,但如果我可以在指令中做到这一点,我发现这是多余的
  • 我可以通过将load() 重命名为loadFor() 来创建解决方法,然后像这样调用它:&lt;div *load="let items for items$"&gt;。不是我要找的东西,所以我会保持这个问题的开放性。
  • 我只是好奇:为什么要创建指令而不是组件?
  • 我想要做的是使用加载的 observable(或观察到的项目)而不手动订阅组件并编写所有样板。我认为如果我只使用异步管道和 ngIf:&lt;div *ngIf="obs$ | async as loaded; else spinner"&gt; &lt;!-- do things with loaded --&gt; &lt;/div&gt; &lt;ng-template #spinner&gt; 会更好。但是,如果我可以编写自己的指令来跳过实现我自己的异步管道,那么这是多余的:&lt;div *load="items$ as items"&gt;。这让我可以在 div 内的任何地方使用items,这是我想要的
  • 没有用于订阅 observables 和存储结果的样板,没有额外的组件,非常漂亮和干净的代码,只订阅一次并且结果存在于范围内。

标签: angular typescript rxjs angular-directive


【解决方案1】:

这是我尝试过的,如果可行,请告诉我。

例如https://stackblitz.com/edit/angular-csogfu

只需对您的代码稍作修改

更改自

this.viewContainer.createEmbeddedView(this.templateRef, {load: this.data});

const viewRef = this.viewContainer.createEmbeddedView(this.templateRef);
viewRef.context.data = this.data;

模板更改为

<div *appLoader="data$; data as result">
    {{result | json}}
</div>

【讨论】:

  • 第一个更改是多余的 - 更新后的模板可以使用任何一种方式。我正在考虑用data$; data as result 之类的方法解决问题,但不确定这是否符合问题的要求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-10
  • 2020-08-16
  • 1970-01-01
  • 1970-01-01
  • 2019-09-18
  • 1970-01-01
相关资源
最近更新 更多