Apply a directive conditionally

2019-02-02 19:54发布

问题:

I am using Material 2 to add md-raised-button.

I want to apply this directive only if certain condition becomes true.

For example:

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

Another example: I created a basic dynamic reactive form in plunker. I am using formArrayName directive of reactive form for array of controls. I want to apply formArrayName directive only if specific condition becomes true. otherwise don't add formArrayName directive. I tried and researched a lot but could find any solution.

Here is plunker link: https://plnkr.co/edit/oPZ7PyBSf8jjYa2KVh4J?p=preview

I would really appreciate any contribution.

Thanks in advance!

回答1:

I don't know if you can apply directives based on a condition, but a workaround would be having 2 buttons and display them based on a condition.

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

Edit: maybe this will be helpful.



回答2:

You can use the below method:

<button [attr.md-raised-button]="condition ? '' : null"></button>

Applied the same to your plunker: fork

Update:

How condition ? '' : null works as the value:

When its the empty string ('') it becomes attr.md-raised-button="", when its null the attribute will not exist.

Update: plunker update: fork (version issues fixed, please note the question was originally based on angular 4)



回答3:

This may come late, but it is a viable and elegant method for applying a directive conditionally.

In the directive class create the input variable:

@Input('myDirective') options: any;

When applying the directive, set the apply property of the input variable:

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

In the method of the directive check for the variable this.options.apply and apply the directive logic based on the condition:

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

    // directive logic
}


回答4:

As others have also stated, directives can't be dynamically applied.

However, if you just want to toggle md-button's style from flat to raised, then this

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

would do the trick. Plunker



回答5:

As already noted this does not appear to be possible. One thing that can be used to at least prevent some duplication is ng-template. This allows you to extract the content of the element affected by the ngIf branching.

If you for example want to create a hierarchical menu component using 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>

Here the conditionally applied directive is matMenuTriggerFor, which should only be applied to menu items with children. The contents of the button are inserted in both places via ngTemplateOutlet.



回答6:

Currently, there is NO way to conditionally apply a directive to a component.This is not supported.The components which you have created can be added or removed conditionally.

There is already an issue created for the same with angular2, so it should be the case with angular4 aswell.

Alternatively you can go for the option with ng-if

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


回答7:

yes it is possible.

html page with appActiveAhover directive :)

  <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

@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');
    }
  }

}


回答8:

I couldn't find a nice existing solution, so i built my own directive which does this.

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);
  }
}

Then your html:

<div dynamic-attr="{{hasMargin: 'margin-left' ? ''}}"></div>



回答9:

This could be a solution too:

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


It's working for angular 4, ionic 3 like this:

[color]="condition ? 'primary' : ''" where condition is a function that decides if this is an active page or not. The whole code look like this:

<button *ngFor="let page of ..." [color]="isActivePage(page) ? 'primary' : ''">{{ page.title }}</button>



回答10:

Use NgClass

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

example of true condition:

this.element === 'Today'

or a boolean function

getTruth()

full example:

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

If you want a default class:

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


回答11:

I got another idea about what you could do.

You could store the html you want replaced in a variable as a string and then add / remove the directive from it as you wish, using the bypassSecurityTrustHtml method of the DomSanitizer.

I doesn't result in a clean solution but at least you don't need to repeat the code.



回答12:

As at 18th Jan 2019, This is how I added a directive conditionally in Angular 5 and above. I needed to change the color of the <app-nav> component based on darkMode. If the page was in dark mode or not.

This worked for me:

<app-nav [color]="darkMode ? 'orange':'green'"></app-nav>

I hope this helps someone.