Apply a directive conditionally
Solution 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.
Solution 2
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 3
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
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
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
user2899728
Updated on July 08, 2022Comments
-
user2899728 almost 2 years
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 applyformArrayName
directive only if specific condition becomes true, otherwise don't addformArrayName
directive.Here is a plunker link.
-
user2899728 almost 7 yearsYes md-raised-button it is attribute directive (material.angular.io/components/component/button)
-
Reactgular over 6 yearsApplying conditions on which directives are used would likely render AoT useless as you wouldn't be able to compile the templates unless the app was running.
-
-
user2899728 almost 7 yearsThis is not what I asked. You are showing example for class. I am talking about attribute-directive.
-
Edric almost 7 years@user2899728 Unfortunately there's no other way to do it. (You can't set
md-raised-button
attribute as false) -
Moshe almost 7 years@user2899728 There is no way to apply directives conditionally. I've attempted to give you what you were looking for (end result) with a different approach (the "means").
-
user2899728 almost 7 yearsThank you for your response. But I already have used this technique in the plunker example which I added in question detail. This is good option but not good idea if code is complex. Because because this technique ruin the concept of reusing. You can see my example in plunker and notice how my code is duplicated because of this approach. That is why I don't want to use this technique. I want some better solution so I can generate dynamic elements with.
-
LLL almost 7 yearsI see. You could wrap the code that is duplicated in component.
-
user2899728 almost 7 yearsIt is good idea but unfortunately it does not work in the case of reactive forms. With reactive forms, another issue appears. If I put input into separate component, then angular would require me to add [formGroup]="form" within that child component as well. But if I do so then my array binding would not work.
-
tam.teixeira over 6 yearsAs a side note, the Renderer is deprecated, instead use Renderer2 : alligator.io/angular/using-renderer2
-
Ravinder Payal over 6 yearsThis is the only answer which let's a developer to use conditional directives as well as code re-usability.
-
Aldracor about 6 years@Luke Yes, if you mean by "(now)" as: since 2017-01-06 which was 5 months before the question was asked. see angular changelog 6th item
-
Arsenii Fomin almost 6 years@Aldracor angular 5.2.1, doesn't work. And I don't event understand why it should look like "condition ? '' : null" where both results are basically false. What should be instead of '' ? 'true' doesn't work also.
-
Aldracor almost 6 years@ArseniiFomin You should ask a new question for that. In regards with how it works; I will edit my answer too explain.
-
Ben Taliadoros over 5 yearsah im trying it on an ng-container, makes sense it wouldnt work in this scenario. i wonder if there's a solution for this case, will post if so
-
Ben Taliadoros over 5 yearsso for anyone who doesnt want to create a html element in order for their directive to work and is using ng-container, i just went with passing in an [enabled]="booleanVar"
-
JeffryHouser over 5 yearsNot working for me in Angular 6. The plunker sample doesn't get past the loading screen. Based on my reading, this approach may work for HTML attributes, but will not work for Angular directives. The original question was about Angular Directives from the ng material library.
-
Ran Lottem over 5 yearsThis doesn't work for me, as the directive still exists in the component, it just performs no logic. I would like to have the directive's existence apply conditionally, specifically because there's css that checks for this directive.
-
Tiha over 5 yearsYou have a different issue, than the one listed here (Apply the directive conditionally). Post your issue and people will help you. As a first idea, you could put the directive on a div and put an ng-container with an *ngIf around it to apply your condition. ngIf removes the div node from the DOM, so it might work. I am just guessing, you need to post your problem with code samples/description for ppl to help you.
-
Reza over 5 yearsseems doesn't work on ng7, and unable to open the plunker, can you please create a stackblitz for it
-
pinguinjkeke over 5 yearsYou can use just a
@HostBinding('attr.dynamic-attr') @Input('dynamic-attr') attr: string;
instead of ngOnInit + ElementRef. -
Aldracor over 5 years@RezaRahmati It is still working. See here as requested.
-
Reza over 5 years@Aldracor Thanks, btw question is about angular directives not simple attributes. please see this stackblitz.com/edit/angular-conditional-directive-2004
-
Aldracor over 5 years@RezaRahmati Yes you'r right. I've added an update so the plunker fork work again. (adds the attribute but the directive is never executed) Have a look here it might solve your problem
-
Chris Haines over 5 yearsThis is not an answer to the question which was how to add a directive dynamically, not an attribute.
-
Shachar Har-Shuv about 5 yearsThat's a terribly solution since it's duplicates code. Imagine it's not just button but a div with a lot of html code inside it.
-
kbpontius about 5 years@RezaRahmati I can verify this solution works on "simple attributes". Maybe this isn't what you were referring to, but for example, on
input
'smultiple
attribute:<input type="file" accept="type/jpg" [attr.multiple]="condition ? true : null"/>
. Hopefully this is helpful to someone coming afterwards. -
Reza about 5 years@kbpontius thanks for comment, it works on Html attributes but not on angular directives (as attribute)
-
kbpontius about 5 years@RezaRahmati Ahh, gotcha, just misunderstood your comment. Thanks for the clarification.
-
Mojtaba about 5 yearsYou apply the directive in both cases just with different options (i.e., orange or green). The question asks for ignoring the directive at all based on a condition.
-
bvdb over 4 yearsWhen you offer a creative alternative, it usually helps to put in a line of comments first to describe where you are going with your answer.
-
Moshe over 4 years@bvdb thank you for your kind words. I was writing without direction, and that was a mistake. In the future, I hope to edit this post, as it could help even one person.
-
Dino over 4 yearsThis is not a good solution. Directive still get's initialized.
-
Rijo over 4 yearsCustom directive won't work for this approch. I have custome directive
'appOnlynumber'
ary = [{numeric: true},{numeric:false}] <div *ngFor="let a of ary"> <input appOnlynumber="(a.numeric) ? '': null" /></div>
In this scenario custom directive won't work -
Jesse over 4 yearsIn my case, this almost works, but my directive was called
appTooltipDirective
and this method put the attributeapptooltipdirective
(no casing) on my element so it didn't work. -
Sumit Ramteke about 4 yearsalready covered in LLL's answer
-
electrocrat over 3 yearsI had to use
undefined
instead ofnull
. -
Oleg K over 3 yearsIt is not necessary to include
formGroup
to a child component that wrapsinput
as you can simply pass yourformControl
as an@Input() control: FormControl
inside the wrapper component and apply it to the nativeinput
via[formControl]="control"
-
masterxilo over 3 yearsthis only works for conditionally removing attributes of the html tag, not for angular directives
-
oomer over 2 yearsUnfortunately, angular has not exposed any elegant way, so we left with this way only.
-
Sixteen over 2 yearsThis solutions is only viable if you work with a custom Directive. If you use a Directive from a library where you can't access logic (or should not touch it), you can't implement this.
-
Timtim over 2 yearsThis solution is really elegant, thank you!
-
DFSFOT about 2 yearsYou'll still have code for two buttons in which case you might as well do
*ngIf="condition"
on it with double code -
DFSFOT about 2 yearsThe nested ng-container confuses me (under Node button), only one is necessary(read sufficient) for this solution right?
-
H.B. about 2 years@DFSFOT One is for the if statement to check for children, the other is for rendering the content template. The key is the template rendering, the rest is for example purposes. (The outer ng-container could also be something else, like a
<div>
, depending on what document structure is desired.) -
Adeel Shekhani about 2 yearsSeems wrong, doesn't look like template is actually need with what you wrote. If template is there it should utilized at least twice in this case
-
B. Feenstra about 2 yearsThis is, however, not an option if your parent container depends on
ContentChildren
. For example, themat-table
component inner elements cannot be put into a template outlet because the required header, row and footer definitions cannot be found. Unfortunately, this is where I am stuck at the moment. and have to duplicate some HTML. -
ZecKa about 2 yearsnote that ripple will not work with only mat-raised-button class