Apply a directive conditionally

AngularAngular MaterialAngular2 FormsAngular2 DirectivesAngular Material2

Angular Problem Overview


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.

Here is a plunker link.

Angular Solutions


Solution 1 - Angular

If you just need to add an attribute in order to trigger CSS rules, you can use the below method: (this does not dynamically create/destroy a directive)

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

Solution 2 - Angular

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.

Solution 3 - Angular

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.

Solution 4 - Angular

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
}

Solution 5 - Angular

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

Solution 6 - Angular

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> 

Solution 7 - Angular

Maybe it will help someone.

In the example below I have the my-button.component.html and I want to apply the *appHasPermission directive to the <button> only if the role attribute is set.

<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>

That way you don't duplicate the <button> code.

Solution 8 - Angular

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>

Solution 9 - Angular

Passing null to the directive removes it!

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

Solution 10 - Angular

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>

Solution 11 - Angular

I am working with Angular Material, adding an element on *ngIf didn't work properly for me (the element would disappear inside many newly generated material HTML tags lol).

I don't know if it's a good practice, but I used OnChanges and I had a sort of conditional directive - and it worked! :)

So this is how I solved it:

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

Solution 12 - Angular

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.

Solution 13 - Angular

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

}

Solution 14 - Angular

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>

Solution 15 - Angular

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.

EDIT

This changes the value of an attribute (color) based on a condition. It just happens that the color is defined using a directive. So anyone reading this please do not get confused, this is not applying a directive conditionally (ie. which means adding or removing a directive to the dom based on a condition)

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser2899728View Question on Stackoverflow
Solution 1 - AngularAldracorView Answer on Stackoverflow
Solution 2 - AngularLLLView Answer on Stackoverflow
Solution 3 - AngularH.B.View Answer on Stackoverflow
Solution 4 - AngularTihaView Answer on Stackoverflow
Solution 5 - AngularAnkit SinghView Answer on Stackoverflow
Solution 6 - AngularSajeetharanView Answer on Stackoverflow
Solution 7 - AngularMhagnumDwView Answer on Stackoverflow
Solution 8 - AngularzalogView Answer on Stackoverflow
Solution 9 - AngularYogesh AggarwalView Answer on Stackoverflow
Solution 10 - AngularCappi10View Answer on Stackoverflow
Solution 11 - AngularItayView Answer on Stackoverflow
Solution 12 - AngularLLLView Answer on Stackoverflow
Solution 13 - AngularChaitanya NekkalapudiView Answer on Stackoverflow
Solution 14 - AngularMosheView Answer on Stackoverflow
Solution 15 - AngularSandra israelView Answer on Stackoverflow