首页 > 解决方案 > 是否可以将后备模板 ref 添加到指令中?

问题描述

在 Angular 8 中,我使用指令来读取存储中的值,而不是根据值呈现 div:

指令:

@Directive({
    selector: '[appOperator]'
})
export class OperatorDirective implements OnInit, OnDestroy {

    private isOperator = false;
    private subscription: Subscription;

    constructor(private elementRef: ElementRef,
                private viewContainer: ViewContainerRef,
                private templateRef: TemplateRef<any>,
                private store$: Store<RootStoreState.IAppState>) { }

    ngOnInit() {
        this.subscription = this.store$.pipe(select(AuthStoreSelectors.isOperator)).subscribe((isOperator) => {
            this.isOperator = isOperator;
            this.setElementOperation();
        });
    }

    setElementOperation(): void {
        if (this.isOperator) {
            this.viewContainer.createEmbeddedView(this.templateRef);
        } else {
            this.viewContainer.clear();
        }
    }

    ngOnDestroy() {
        this.subscription.unsubscribe();
    }
}

用例:

      <app-name-input
        class="header_name"
        [name]="waypoint.name"
        (nameChange)="applyRename(waypoint, $event)"
        *appOperator
      ></app-name-input>

我想要某种后备模板,以防原始内容被隐藏。所以我试图在这种情况下显示的指令中添加一个模板回退:

我补充说:

@Input() fallBackTemplateRef: TemplateRef<any>;
setElementOperation(): void {
    if (this.isOperator) {
        this.viewContainer.createEmbeddedView(this.templateRef);
    } else {
        this.viewContainer.clear();

        if(this.fallBackTemplateRef) {
            this.viewContainer.createEmbeddedView(this.fallBackTemplateRef);
        }
    }
}

我正在尝试按如下方式使用它

      <ng-template #myTemplate let-waypoint="waypoint">
        <div class="header_name">{{waypoint.name}}</div>
      </ng-template>
      <app-name-input
        class="header_name"
        [name]="waypoint.name"
        (nameChange)="applyRename(waypoint, $event)"
        appOperator
        *appOperator
        fallBackTemplateRef="myTemplate"
      ></app-name-input>

我面临的问题是这个编译但在应用程序中不起作用,fallBackTemplateRef总是未定义,我什么也画不出来

是否有可能实现我想要做的事情?我错过了什么?

标签: angular

解决方案


在您的版本中,您没有绑定到fallbackTemplateRef. 并且尝试使用该[fallbackTemplateRef]语法将尝试绑定到组件上的输入属性。

要绑定到结构指令上的其他属性,您可以*ngFor举个例子。

*ngFor="let item of collection;trackBy: trackByFn"

源代码中,这是通过创建一个以指令选择器为前缀的输入属性来实现的:

@Input() set ngForTrackBy(fn: TrackByFn) {  
  this._trackByFn = fn;
}

解决方案

演示:https ://stackblitz.com/edit/router-template-vm1hsv

我已修改您的指令以采用以下样式的附加输入属性*ngForTrackBy

import { Directive, OnInit, OnDestroy, ElementRef, ViewContainerRef, TemplateRef, Input } from '@angular/core';

import { Subject, interval } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Directive({
  selector: '[appOperator]'
})
export class OperatorDirective implements OnInit, OnDestroy {  

  constructor(private elementRef: ElementRef,
    private viewContainer: ViewContainerRef,
    private templateRef: TemplateRef<any>
  ) {     
  }

  // dummy default input
  @Input('appOperator') dummy: string;
  @Input('appOperatorFallback') fallback: TemplateRef<any>;

  private isOperator = false;
  private destroyed: Subject<void> = new Subject<void>();

  ngOnInit() {      
    interval(1000).pipe(
      takeUntil(this.destroyed)
    ).subscribe(() => {
      this.isOperator = !this.isOperator;
      this.setElementOperation();
    });

    this.setElementOperation();
  }

  setElementOperation(): void {
    this.viewContainer.clear();

    if (this.isOperator) {
      this.viewContainer.createEmbeddedView(this.templateRef);
    } else if(this.fallback) {      
      this.viewContainer.createEmbeddedView(this.fallback);
    }
  }

  ngOnDestroy() {
    this.destroyed.next();
    this.destroyed.complete();
  }
}

我使用了一个 rxjsinterval来创建一个工作演示。你可以用你的 observable 替换它。

然后在 html 中使用,如下所示:

<ng-template #myfallback>
  <div>
    FALLBACK
  </div>
</ng-template>

<other *appOperator="'';fallback:myfallback">
</other>

<other>其他组件在哪里。

细分'';fallback:myfallback是这样的:

  • ''- 这是@Input('appOperator')虚拟输入属性的输入。它可以是除了空白之外的任何东西。我选择了一个空字符串,但它也可能是一个未声明的变量:_;fallback:myfallback
  • ;- 输入属性分隔符
  • fallback:myfallback正在将模板传递#myfallback@Input('appOperatorFallback') fallback: TemplateRef<any>;属性

我的问题是这*appLoading="'';fallback:fallback"很难看。我选择将默认输入保留为虚拟输入,因为考虑到后备不是操作员指令的主要输入值,这没有任何意义。

在我的研究中,我找不到一种方法来指定额外的输入,而不在那个初始位置指定一些东西。

如果您只有一个输入并且不喜欢我不情愿地选择的语法,您总是可以将后备作为主要输入传递:

@Input('appOperator') fallback: TemplateRef<any>;

然后它只是像这样使用它的一个例子:

*appOperator="myfallback"

对我来说,这有一些认知失调,但至少它很漂亮:)


推荐阅读