【问题标题】:Can I do two things at once in a structural directive?我可以在结构指令中同时做两件事吗?
【发布时间】:2019-04-18 21:45:38
【问题描述】:

我想创建一个结构指令,其行为如下:

<p *myDirective="condition">This is some text</p>
  • 如果conditionfalse,则&lt;p&gt; 标记根本呈现。
  • 如果conditiontrue,则&lt;p&gt; 标记将使用额外的class 属性呈现。

所以,要么什么都没有渲染,要么:

<p class="my-added-class">This is some text</p>

换句话说,它有点像*ngIf,但有额外的行为。

我可以找到如何执行包含/排除行为的示例(实际上有这样的示例in the Angular docs)。我还可以找到如何将类添加到元素 using the Renderer2 API 的示例。

但是,我不知道如何结合这些技术,因为第一种方法操纵 viewContainer 以创建嵌入式 视图,而第二种方法使用渲染器来操纵 元素。

有没有办法做到这一点?我可以以某种方式创建嵌入式视图,然后操纵它创建的元素吗?或者我可以操纵模板来改变视图的呈现方式吗?

[注意:@HostBinding 不适用于结构指令,因此这不是一个选项]

【问题讨论】:

  • 您为什么不想只使用*ngIfngClass 并根据条件执行必要的操作?类似&lt;p *ngIf="condition" [class.yourClass]="condition"&gt;This is some text&lt;/p&gt;
  • 创建嵌入视图时,可以通过rootNodes属性抓取渲染的元素:const viewRef = this.viewContainer.createEmbeddedView(this.templateRef); viewRef.rootNodes[0].classList.add('my-added-class'); Demo:stackblitz.com/edit/angular-1mdlht
  • @SiddAjmera:Errrm……因为我想将这两件事一起包装在一个指令中?所以每次我使用它时,我只需要输入一个指令,它就会为我做所有事情。我的意思是,我可以只输入所有内容,但是……我可以只使用 HTML :-)
  • @AlexK:看起来很完美 - 谢谢!

标签: angular angular-directive


【解决方案1】:

当 DOM 满足传递给它的表达式(在 setter 内部)时,我会考虑在 DOM 上添加类。您可以在指令中获取 ElementRef 依赖项,并将 class 附加到它的真实值。

@Input() set myDirective(condition: boolean) {
  if (condition) {
    this.viewContainer.createEmbeddedView(this.templateRef);
    this.elementRef.nativeElement.nextElementSibling.classList.add('my-added-class'); // renderer API can be used here
    // as Alex and Yurzui suggested
    // const view = this.viewContainer.createEmbeddedView(this.templateRef); 
    // view.rootNodes[0].classList.add('some-class')
  } else if (condition) {
    this.viewContainer.clear();
  }
}

【讨论】:

  • 试过了。但它给出了一个错误说ERROR TypeError: Cannot read property 'add' of undefined
  • 别忘了结构指令 elementRef 指的是注释节点
  • 另一种方式是const view = this.viewContainer.createEmbeddedView(this.templateRef); view.rootNodes[0].classList.add('some-class'),但我们应该确切地知道模板包含顶层元素
【解决方案2】:

另一种方式

只是玩玩:)

使用 Renderer2 是普遍安全的

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

@Directive({
  selector: '[appMy]'
})
export class MyDirective implements OnInit{
  constructor(
  private templateRef: TemplateRef<any>,
  private viewContainer: ViewContainerRef,
  private renderer: Renderer2) { }
  @Input() set appMy(condition: boolean) {
   if (condition) {
     this.viewContainer.createEmbeddedView(this.templateRef);
    } else  {
     this.viewContainer.clear();
   }
  }
  ngOnInit() {
    const elementRef = this.viewContainer.get(0).rootNodes[0] as ElementRef;
    this.renderer.addClass(elementRef, 'myclass');
  }
}

遵循@Pankaj 方式但使用渲染器

@Input() set appMy(condition: boolean) {
   if (condition) {
     const view = this.viewContainer.createEmbeddedView(this.templateRef);
     this.renderer.addClass(view.rootNodes[0], 'myclass');
   } else  {
     this.viewContainer.clear();
   }
  }

【讨论】:

  • 谢谢。我接受了您的第二个建议,因为它将所有内容都放在一个地方,并且因为它不涉及注入 ElementRef 等。需要注意的一点是它适用于 &lt;p *myDirective="condition"&gt;text&lt;/p&gt;不适用于@ 987654324@ 因为在后一种情况下没有元素可以添加类。 (我可以忍受)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多