How to add conditional attribute in Angular 2?
JavascriptAngularJavascript Problem Overview
How can I conditionally add an element attribute e.g. the checked
of a checkbox?
Previous versions of Angular had NgAttr
and I think NgChecked
which all seem to provide the functionality that I'm after. However, these attributes do not appear to exist in Angular 2 and I see no other way of providing this functionality.
Javascript Solutions
Solution 1 - Javascript
null
removes it:
[attr.checked]="value ? '' : null"
or
[attr.checked]="value ? 'checked' : null"
##Hint:
Attribute vs property
When the HTML element where you add this binding does not have a property with the name used in the binding (checked
in this case) and also no Angular component or directive is applied to the same element that has an @Input() checked;
, then [xxx]="..."
can not be used.
What to bind to when there is no such property
Alternatives are [style.xxx]="..."
, [attr.xxx]="..."
, [class.xxx]="..."
depending on what you try to accomplish.
Because <input>
only has a checked
attribute, but no checked
property [attr.checked]="..."
is the right way for this specific case.
Attributes can only handle string values
A common pitfall is also that for [attr.xxx]="..."
bindings the value (...
) is always stringified. Only properties and @Input()
s can receive other value types like boolean, number, object, ...
Most properties and attributes of elements are connected and have the same name.
Property-attribute connection
When bound to the attribute the property also only receives the stringified value from the attribute.
When bound to the property the property receives the value bound to it (boolean, number, object, ...) and the attribute again the stringified value.
Two cases where attribute and property names do not match.
-
https://developer.mozilla.org/en-US/docs/Web/API/HTMLLabelElement/htmlFor (see the first sentence of the description
htmlFor property reflects the value of the for
-for
probably didn't work because it's a keyword in C or JavaScript)
Angular was changed since then and knows about these special cases and handles them so that you can bind to <label [for]="
even though no such property exists (same for colspan
)
Solution 2 - Javascript
in angular-2 attribute syntax is
<div [attr.role]="myAriaRole">
Binds attribute role to the result of expression myAriaRole.
so can use like
[attr.role]="myAriaRole ? true: null"
Solution 3 - Javascript
Refining Günter Zöchbauer answer:
This appears to be different now. I was trying to do this to conditionally apply an href attribute to an anchor tag. You must use undefined for the 'do not apply' case. As an example, I'll demonstrate with a link conditionally having an href attribute applied.
--EDIT--
It looks like angular has changed some things, so null
will now work as expected. I've update the example to use null
rather than undefined
.
An anchor tag without an href attribute becomes plain text, indicating a placeholder for a link, per the hyperlink spec.
For my navigation, I have a list of links, but one of those links represents the current page. I didn't want the current page link to be a link, but still want it to appear in the list (it has some custom styles, but this example is simplified).
<a [attr.href]="currentUrl !== link.url ? link.url : null">
This is cleaner than using two *ngIf's on a span and anchor tag, I think. It's also perfect for adding a disabled attribute to a button.
Solution 4 - Javascript
If you need exactly checked
, it's much better to use property instead of the attribute and assign some boolean value to it:
<input type="checkbox" [checked]="smth">
If you want to conditionally add any arbitrary attribute, you can use binding to it:
<p [attr.data-smth]="smth">
- If the value is
undefined
ornull
attribute is not added. - If the value is an empty string, attribute is added without the value.
- Otherwise attribute is added with a stringified value.
Of course, you can use it with attr.checked
too:
<input type="checkbox" [attr.checked]="smth ? '' : null">
But note that in such case attr is NOT synchronized with being checked, so following combination
<input type="checkbox" [checked]="false" [attr.checked]="''">
will lead to unchecked checkbox with checked
attribute.
And a complete example:
import { Component } from "@angular/core";
@Component({
selector: "app-root",
template: `
<p [attr.data-smth]="e">The value is: {{what(e)}}</p>
<p [attr.data-smth]="s">The value is: {{what(s)}}</p>
<p [attr.data-smth]="u">The value is: {{what(u)}}</p>
<p [attr.data-smth]="n">The value is: {{what(n)}}</p>
<p [attr.data-smth]="t">The value is: {{what(t)}}</p>
<p [attr.data-smth]="f">The value is: {{what(f)}}</p>
<input type="checkbox" [checked]="t">
<input type="checkbox" [checked]="f">
<input type="checkbox" [attr.checked]="t ? '' : null">
<input type="checkbox" [attr.checked]="f ? '' : null">
<input type="checkbox" [checked]="false" [attr.checked]="''">
`,
styles: [`
p[data-smth] {
color: blue;
}
`]
})
export class AppComponent {
e = ""
s = "abc"
u = undefined
n = null
t = true
f = false
what(x) {
return typeof x === 'string' ? JSON.stringify(x) : x + ""
}
}
That's the result:
and the markup:
Solution 5 - Javascript
If it's an input element you can write something like....
<input type="radio" [checked]="condition">
The value of condition must be true or false.
Also for style attributes...
<h4 [style.color]="'red'">Some text</h4>
Solution 6 - Javascript
you can use this.
<span [attr.checked]="val? true : false"> </span>
Solution 7 - Javascript
You can use a better approach for someone writing HTML for an already existing scss.
html
[attr.role]="<boolean>"
scss
[role = "true"] { ... }
That way you don't need to <boolean> ? true : null
every time.
Solution 8 - Javascript
I wanted to have tooltip only for a particular field as added below in code but you want to have tooltip on multiplent you can have an array valdidate using:
Multiple Elements haveing custom data-tooltip attribute:
1: ['key1ToHaveTooltip', `key2ToHaveTooltip'].includes(key)
2: ['key1ToHaveTooltip', 'key2ToHaveTooltip'].indexOf(key) > -1
to have tooltip attribute on more than 1 element.
<div *ngFor="let key of Keys"
[attr.data-tooltip]="key === 'IwantOnlyThisKeyToHaveTooltipAttribute'
? 'Hey! I am a tooltip on key matched'
: null">
</div>
Solution 9 - Javascript
Here, the paragraph is printed only 'isValid' is true / it contains any value
<p *ngIf="isValid ? true : false">Paragraph</p>
Solution 10 - Javascript
Inline-Maps are handy, too.
They're a little more explicit & readable as well.
[class]="{ 'true': 'active', 'false': 'inactive', 'true&false': 'some-other-class' }[ trinaryBoolean ]"
Just another way of accomplishing the same thing, in case you don't like the ternary syntax or ngIf
s (etc).