【问题标题】:Apply a directive conditionally有条件地应用指令
【发布时间】:2017-11-19 16:38:21
【问题描述】:

我正在使用 Material 2 添加md-raised-button。我只想在某些条件为真时应用此指令。

例如:

<button md-raised-button="true"></button>

另一个例子: 我在 plunker 中创建了一个基本的动态反应形式。 我正在为控件数组使用反应形式的formArrayName 指令。 我只想在特定条件为真时应用formArrayName 指令,否则不要添加formArrayName 指令。

这是plunker link

【问题讨论】:

标签: angular angular-material angular2-forms angular2-directives angular-material2


【解决方案1】:

如果你只需要添加一个属性来触发 CSS 规则,你可以使用下面的方法:(这不会动态创建/销毁指令)

&lt;button [attr.md-raised-button]="condition ? '' : null"&gt;&lt;/button&gt;

对你的 plunker 应用同样的:fork

更新:

condition ? '' : null 如何作为值工作:

当它的空字符串('')变成attr.md-raised-button="",当它的null属性将不存在。

更新: plunker 更新:fork(版本问题已修复,请注意问题最初基于 angular 4)

【讨论】:

  • @Luke 是的,如果您的意思是 "(now)" 为:自 2017-01-06 起,即提出问题前 5 个月。 angular changelog第6条
  • @Aldracor angular 5.2.1,不起作用。而且我不明白为什么它应该看起来像“条件?'':null”,其中两个结果基本上都是错误的。应该用什么代替 '' ? 'true' 也不起作用。
  • 所以对于那些不想创建 html 元素以使其指令正常工作并使用 ng-container 的人,我只是传入一个 [enabled]="booleanVar"
  • 在 Angular 6 中不适合我。plunker 示例无法通过加载屏幕。根据我的阅读,这种方法可能适用于 HTML 属性,但不适用于 Angular 指令。最初的问题是关于 ng 材料库中的 Angular 指令。
  • @kbpontius 感谢您的评论,它适用于 Html 属性,但不适用于角度指令(作为属性)
【解决方案2】:

我不知道您是否可以根据条件应用指令,但解决方法将有 2 个按钮并根据条件显示它们。 p>

<button *ngIf="!condition"></button>
<button *ngIf="condition" md-raised-button></button> 

编辑:也许this 会有所帮助。

【讨论】:

  • 感谢您的回复。但是我已经在我详细添加的问题示例中使用了这种技术。如果代码很复杂,这是一个不错的选择,但不是一个好主意。因为因为这种技术破坏了重用的概念。您可以在 plunker 中查看我的示例,并注意我的代码是如何因为这种方法而重复的。这就是为什么我不想使用这种技术。我想要一些更好的解决方案,以便生成动态元素。
  • 我明白了。您可以包装组件中重复的代码。
  • 这是个好主意,但不幸的是它不适用于反应形式。对于反应形式,出现了另一个问题。如果我将输入放入单独的组件中,那么角度将要求我在该子组件中添加 [formGroup]="form" 。但如果我这样做了,那么我的数组绑定将不起作用。
  • 这是一个非常糟糕的解决方案,因为它是重复代码。想象一下,它不仅仅是一个按钮,而是一个 div,里面有很多 html 代码。
  • 很遗憾,angular 并没有暴露出优雅的方式,所以我们只留下了这种方式。
【解决方案3】:

如前所述,这似乎是不可能的。至少可以用来防止某些重复的一件事是ng-template。这允许您提取受ngIf 分支影响的元素的内容。

例如,如果您想使用 Angular Material 创建分层菜单组件:

<!-- Button contents -->
<ng-template #contentTemplate>
    <mat-icon *ngIf="item.icon != null">{{ item.icon }}</mat-icon>
    {{ item.label }}
</ng-template>

<!-- Leaf button -->
<button *ngIf="item.children == null" mat-menu-item
    (click)="executeCommand()"
    [disabled]="enabled == false">
    <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
</button>
<!-- Node button -->
<ng-container *ngIf="item.children != null">
    <button mat-menu-item
        [matMenuTriggerFor]="subMenu">
        <ng-container *ngTemplateOutlet="contentTemplate"></ng-container>
    </button>

    <mat-menu #subMenu="matMenu">
        <menu-item *ngFor="let child of item.children" [item]="child"></menu-item>
    </mat-menu>
</ng-container>

这里有条件应用的指令是matMenuTriggerFor,它应该只应用于带有子项的菜单项。按钮的内容通过ngTemplateOutlet插入到这两个地方。

【讨论】:

  • 这是让开发人员使用条件指令以及代码可重用性的唯一答案。
  • 这个解决方案真的很优雅,谢谢!
【解决方案4】:

这可能来晚了,但它是有条件地应用指令的可行且优雅的方法。

在指令类中创建输入变量:

@Input('myDirective') options: any;

应用指令时,设置输入变量的apply属性:

<div [myDirective] = {apply: someCondition}></div>

在指令的方法中检查变量 this.options.apply 并根据条件应用指令逻辑:

ngAfterViewInit(): void {
    if (!this.options.apply) {
        return;
    }

    // directive logic
}

【讨论】:

  • 这对我不起作用,因为该指令仍然存在于组件中,它只是不执行任何逻辑。我希望有条件地应用指令的存在,特别是因为有 css 来检查这个指令。
  • 您遇到的问题与此处列出的问题不同(有条件地应用指令)。发布您的问题,人们会帮助您。作为第一个想法,您可以将指令放在 div 上,并在其周围放置一个带有 *ngIf 的 ng-container 以应用您的条件。 ngIf 从 DOM 中删除 div 节点,因此它可能会起作用。我只是在猜测,您需要将您的问题与代码示例/描述一起发布以供 ppl 帮助您。
  • 这不是一个好的解决方案。指令仍然被初始化。
  • 此解决方案仅在您使用自定义指令时才可行。如果您使用无法访问逻辑(或不应该触摸它)的库中的指令,则无法实现。
【解决方案5】:

正如其他人所说,directives 不能动态应用。

但是,如果您只想将md-button 的样式从 flat 切换为 raised,那么这个

<button md-button [class.mat-raised-button]="isRaised">Toggle Raised Button</button>

会成功的。 Plunker

【讨论】:

    【解决方案6】:

    目前,有 NO 方法可以有条件地将指令应用于组件。不支持此功能。您创建的组件可以有条件地添加或删除。

    angular2 已经存在同样的问题,因此 angular4 也应该是这种情况。

    或者,您可以选择使用 ng-if 的选项

    <button ngIf="!condition"></button>
    <button ngIf="condition" md-raised-button></button> 
    

    【讨论】:

    【解决方案7】:

    也许它会对某人有所帮助。

    在下面的示例中,我有 my-button.component.html,并且我想仅在设置了 role 属性的情况下将 *appHasPermission 指令应用于 &lt;button&gt;

    <ng-container *ngIf="role; else buttonNoRole" >
      <ng-container *appHasPermission="role">
        <!-- button with *appHasPermission -->
        <ng-template *ngTemplateOutlet="buttonNoRole;"></ng-template>
      </ng-container>
    </ng-container>
    
    <ng-template #buttonNoRole>
      <!-- button without *appHasPermission -->
      <button
        mat-raised-button type="button"
        [color]="color"
        [disabled]="disabled"
        [(appClickProgress)]="onClick"
        [key]="progressKey">
        <mat-icon *ngIf="icon">{{ icon }}</mat-icon> {{ label }}
      </button>
    </ng-template>
    

    这样您就不会重复&lt;button&gt; 代码。

    【讨论】:

      【解决方案8】:

      这也可能是一个解决方案:

      [md-raised-button]="condition ? 'true' : ''"


      它适用于角度 4,离子 3,如下所示:

      [color]="condition ? 'primary' : ''" 其中condition 是一个决定这是否是活动页面的函数。整个代码如下所示:

      &lt;button *ngFor="let page of ..." [color]="isActivePage(page) ? 'primary' : ''"&gt;{{ page.title }}&lt;/button&gt;

      【讨论】:

        【解决方案9】:

        null 传递给指令会删除它!

        <button md-raised-button="condition ? true : null"></button>
        

        【讨论】:

        • 这仅适用于有条件地删除 html 标签的属性,不适用于角度指令
        【解决方案10】:

        我找不到一个好的现有解决方案,所以我构建了自己的指令来执行此操作。

        import { Directive, ElementRef, Input } from '@angular/core';
        
        @Directive({
          selector: '[dynamic-attr]'
        })
        export class DynamicAttrDirective {
          @Input('dynamic-attr') attr: string;
          private _el: ElementRef;
        
          constructor(el: ElementRef) {
            this._el = el;
          }
        
          ngOnInit() {
            if (this.attr === '') return null;
            const node = document.createAttribute(this.attr);
            this._el.nativeElement.setAttributeNode(node);
          }
        }
        

        然后你的html:

        &lt;div dynamic-attr="{{hasMargin: 'margin-left' ? ''}}"&gt;&lt;/div&gt;

        【讨论】:

        • 您可以只使用 @HostBinding('attr.dynamic-attr') @Input('dynamic-attr') attr: string; 而不是 ngOnInit + ElementRef。
        • 这不是对如何动态添加指令而不是属性的问题的答案。
        【解决方案11】:

        我正在使用 Angular Material,在 *ngIf 上添加一个元素对我来说不起作用(该元素会在许多新生成的 HTML 标签中消失,哈哈)。

        我不知道这是否是一个好习惯,但我使用了OnChanges 并且我有一种条件指令 - 它起作用了! :)

        所以我就是这样解决的:

        import { Directive, Renderer2, ElementRef, Input, OnChanges, SimpleChanges, AfterViewInit } from '@angular/core';
        
        @Directive({
          selector: '[appDirtyInputIndicator]'
        })
        export class DirtyInputIndicatorDirective implements OnChanges, AfterViewInit {
        
          @Input('appDirtyInputIndicator') dirtyInputIndicator: boolean;
          span = this.renderer.createElement('span');
        
          constructor(private renderer: Renderer2, private el: ElementRef) {}
        
          ngOnChanges(changes: SimpleChanges): void {
            if (changes.dirtyInputIndicator && this.dirtyInputIndicator) {
              this.renderer.appendChild(this.el.nativeElement, this.span);
            } else {
              this.renderer.removeChild(this.el.nativeElement, this.span);
            }
          }
        
          ngAfterViewInit() {
            this.renderer.addClass(this.span, 'dirty_input_badge');
          }
        }
        

        【讨论】:

          【解决方案12】:

          我对你能做什么有了另一个想法。

          您可以将要替换的 html 作为字符串存储在变量中,然后使用 DomSanitizerbypassSecurityTrustHtml 方法从其中添加/删除指令。

          我没有得到一个干净的解决方案,但至少你不需要重复代码。

          【讨论】:

            【解决方案13】:

            是的,这是可能的。

            带有 appActiveAhover 指令的 html 页面 :)

              <li routerLinkActive="active" #link1="routerLinkActive">
                    <a [appActiveAhover]='link1.isActive?false:true' routerLink="administration" [ngStyle]="{'background':link1.isActive?domaindata.get_color3():none}">
                      <i class="fa fa-users fa-lg" aria-hidden="true"></i> Administration</a>
                  </li>
                  <li  routerLinkActive="active" #link2="routerLinkActive">
                    <a [appActiveAhover]='link2.isActive?false:true' routerLink="verkaufsburo" [ngStyle]="{'background':link2.isActive?domaindata.get_color3():none,'color':link2.isActive?color2:none}">
                      <i class="fa fa-truck fa-lg" aria-hidden="true"></i> Verkaufsbüro</a>
                  </li>
                  <li  routerLinkActive="active" #link3="routerLinkActive">
                    <a [appActiveAhover]='link3.isActive?false:true' routerLink="preisrechner" [ngStyle]="{'background':link3.isActive?domaindata.get_color3():none}">
                      <i class="fa fa-calculator fa-lg" aria-hidden="true" *ngIf="routerLinkActive"></i> Preisrechner</a>
                  </li>
            

            指令

            @Directive({
              selector: '[appActiveAhover]'
            })
            export class ActiveAhoverDirective implements OnInit {
              @Input() appActiveAhover:boolean;
              constructor(public el: ElementRef, public renderer: Renderer, public domaindata: DomainnameDataService) {
            }
            
              ngOnInit() {
              }
            
              @HostListener('mouseover') onMouseOver() {
                if(this.appActiveAhover){
                  this.renderer.setElementStyle(this.el.nativeElement, 'color', this.domaindata.domaindata.color2);
                }
              }
            
              @HostListener('mouseout') onMouseOut() {
                if(this.appActiveAhover){
                  this.renderer.setElementStyle(this.el.nativeElement, 'color', 'white');
                }
              }
            
            }
            

            【讨论】:

            【解决方案14】:

            使用NgClass

            [ngClass]="{ 'mat-raised-button': trueCondition }"
            

            真实条件示例:

            this.element === 'Today'
            

            或布尔函数

            getTruth()
            

            完整示例:

              <button [ngClass]="{ 'mat-raised-button': trueCondition }">TEXT</button>
            

            如果你想要一个默认类:

              <button [ngClass]="{ 'mat-raised-button': trueCondition, 'default-class': !trueCondition }">TEXT</button>
            

            【讨论】:

            • 这不是我问的。您正在展示课堂示例。我说的是属性指令。
            • @user2899728 不幸的是,没有其他方法可以做到这一点。 (不能将md-raised-button属性设置为false)
            • @user2899728 无法有条件地应用指令。我试图用不同的方法(“手段”)为您提供您正在寻找的东西(最终结果)。
            • 当您提供有创意的替代方案时,通常最好先放入一行 cmets 来描述您的答案。
            • @bvdb 谢谢你的客气话。我在没有方向的情况下写作,这是一个错误。将来,我希望编辑这篇文章,因为它甚至可以帮助一个人。
            【解决方案15】:

            截至 2019 年 1 月 18 日, 这就是我在 Angular 5 及更高版本中有条件地添加指令的方式。我需要根据darkMode 更改&lt;app-nav&gt; 组件的颜色。页面是否处于暗模式。

            这对我有用:

            &lt;app-nav [color]="darkMode ? 'orange':'green'"&gt;&lt;/app-nav&gt;

            我希望这对某人有所帮助。

            编辑

            这会根据条件更改属性(颜色)的值。碰巧颜色是使用指令定义的。所以任何阅读本文的人请不要感到困惑,这不是有条件地应用指令(即,这意味着根据条件向 dom 添加或删除指令)

            【讨论】:

            • 您在两种情况下都应用指令,只是使用不同的选项(即橙色或绿色)。该问题要求根据条件完全忽略指令。
            猜你喜欢
            • 1970-01-01
            • 2015-07-11
            • 1970-01-01
            • 2019-03-27
            • 2014-11-02
            • 2016-08-29
            • 1970-01-01
            • 2019-07-02
            • 2011-12-15
            相关资源
            最近更新 更多