388

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.

Michel
  • 26,600
  • 6
  • 64
  • 69
Jon Miles
  • 9,605
  • 11
  • 46
  • 66
  • 1
    See also: http://stackoverflow.com/questions/36240736/is-it-possible-to-conditionally-display-element-attributes-using-angular2 – Matty Jan 05 '17 at 13:33
  • See also: https://stackoverflow.com/questions/42179150/how-to-disable-a-input-in-angular2/43765804 – aloisdg Sep 28 '18 at 11:28
  • 2
    googlers, in case you want a value-less attribute to an angular component, [see here](https://stackoverflow.com/a/43990083/444255) – Frank N Apr 10 '19 at 09:23

10 Answers10

779

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.

See also What is the difference between properties and attributes in HTML?

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.

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)

Johan Persson
  • 1,875
  • 11
  • 11
Günter Zöchbauer
  • 623,577
  • 216
  • 2,003
  • 1,567
49

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"
Shaishab Roy
  • 16,335
  • 7
  • 50
  • 68
  • 11
    The problem here is that when `your expression` is `false` the attribute in the DOM will look like `
    `. I don't think this is the intended effect.
    – Günter Zöchbauer Apr 20 '16 at 13:49
  • 8
    Thanks but I asked how to conditionally add the attribute, not conditionally assign it's value. – Jon Miles Apr 20 '16 at 13:56
  • You can even omit the "attr." prefix. – Filip Stachowiak Jun 09 '17 at 11:58
  • 2
    @FilipStachowiak then it won't become an attribute. There are various properties on many elements where the element itself reflects the value bound to a property to an attribute (and also the other direction). In this case you don't need `attr.`. But if you want to add a custom attribute, you definitely need the `attr.` prefix – Günter Zöchbauer Jun 09 '17 at 15:05
  • oh man, this : null is amazing, thing knew this can be used in conditional! thanks for the solution. – pinarella Mar 16 '20 at 15:06
21

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.

Ragnaraxis
  • 332
  • 2
  • 8
12

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 or null 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:

result screenshot

and the markup:

markdown screenshot

Qwertiy
  • 19,681
  • 15
  • 61
  • 128
8

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>

Rohit Tirmanwar
  • 149
  • 1
  • 4
5

you can use this.

<span [attr.checked]="val? true : false"> </span>

WapShivam
  • 964
  • 12
  • 20
  • You checked your attribute value with 'val'. if it gets true then simply true returns otherwise false value return – WapShivam Apr 10 '19 at 11:15
1

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.

João Ghignatti
  • 2,281
  • 1
  • 13
  • 25
  • 1
    This doesn't work in every case. For example it doesn't work with `multiple` attribute on `select` element. – smartmouse Apr 27 '20 at 12:38
0

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>
khizer
  • 1,242
  • 15
  • 13
-3

Here, the paragraph is printed only 'isValid' is true / it contains any value

<p *ngIf="isValid ? true : false">Paragraph</p>
-8

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 ngIfs (etc).

Cody
  • 9,785
  • 4
  • 61
  • 46
  • 2
    A class is something entirely different than a attribute (what the question is about) – Günter Zöchbauer Jan 25 '18 at 10:35
  • 2
    You could also take the effort to make it a proper answer to the question then I had a reason to reconsider my vote and I also could have downvoted without adding a comment to keep you in the dark about what the downvote was for. – Günter Zöchbauer Mar 22 '18 at 18:44
  • 1
    @Cody can you explain (give example code) how your method will work with `checked` attribute (add/remove it from input tag) in `` ? (if we change `class` to `checkbox` in your code it does NOT work (I check this)) – Kamil Kiełczewski Sep 20 '18 at 08:15