How to add conditional attribute in Angular 2?
Asked Answered
F

10

398

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.

Flesher answered 20/4, 2016 at 13:42 Comment(3)
See also: #36241236Zingale
See also: #42179650Stour
googlers, in case you want a value-less attribute to an angular component, see hereExplicable
V
793

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)

Victualer answered 20/4, 2016 at 13:43 Comment(10)
You can omit the "attr." prefix.Cyano
@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. prefixLakshmi
To be clear, you need the attr. portion of this. I was trying to set [href] and only wanted it to be set if the item's href property existed. Without [attr.href] this would add an href with a value of 'null', which, when clicked, would refresh the current page. [attr.href] fixed it.Reporter
@Reporter that doesn't sound right to me. If you set href on an element that has the href property, there is no need to use attr. There must be something else gone wrong.Lakshmi
@günter-zöchbauer Well, I'm using Ng 4.x, and <a [href]="item.href"> where item.href is undefined (not the value undefined, but the property href doesn't exist on item) was setting the href attribute to null. I didn't delve into the source, because honestly I don't much care at this point (this is a tiny detail), but attr. is a different directive altogether and therefore it acting different comes as no surprise to me.Reporter
attr. isn't a directive. It's special Angular syntax. I don't think I fully understand your comment. Perhaps you want <a [href]="item?.href" to prevent errors when item is initially undefined or null.Lakshmi
An additional limitation or distinction hinted at in the Angular docs regarding attr. syntax: "You then set the attribute value, using an expression that resolves to a string." Though null resolves, conditionally removing an attribute, it seems I was unable to use this approach to provide non-string data binding to a 3rd party module's custom configuration attribute (i.e. sortablejs and sortablejsOptions for angular-sortablejs) while trying to conditionally provide attributes with this approach.Lurlenelurline
Attributes always only can be strings. If you want other types (objects) you need property binding (without attr.)Lakshmi
Can I conditionally add property binding to inputs?Nicolas
@Nicolas I don't think so. Can you please elaborate on the use case?Lakshmi
S
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"
Scuttlebutt answered 20/4, 2016 at 13:48 Comment(5)
The problem here is that when your expression is false the attribute in the DOM will look like <div checked="false">. I don't think this is the intended effect.Lakshmi
Thanks but I asked how to conditionally add the attribute, not conditionally assign it's value.Flesher
You can even omit the "attr." prefix.Cyano
@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. prefixLakshmi
oh man, this : null is amazing, thing knew this can be used in conditional! thanks for the solution.Zsa
T
22

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.

Tranquillity answered 24/7, 2018 at 17:42 Comment(0)
K
13

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

Knowles answered 9/8, 2021 at 12:24 Comment(0)
N
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>

Newcastle answered 1/3, 2018 at 22:56 Comment(0)
A
5

you can use this.

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

Aleutian answered 10/4, 2019 at 10:13 Comment(1)
You checked your attribute value with 'val'. if it gets true then simply true returns otherwise false value returnAleutian
D
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.

Discontinue answered 28/10, 2019 at 19:17 Comment(1)
This doesn't work in every case. For example it doesn't work with multiple attribute on select element.Reimport
R
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>
Riesling answered 16/6, 2020 at 16:9 Comment(0)
H
-3

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

<p *ngIf="isValid ? true : false">Paragraph</p>
Heresiarch answered 8/2, 2020 at 14:11 Comment(1)
This isn't his question. This is unhelpful and adds to confusion.Mouflon
D
-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).

Darcie answered 5/9, 2017 at 22:6 Comment(3)
A class is something entirely different than a attribute (what the question is about)Lakshmi
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.Lakshmi
@Darcie can you explain (give example code) how your method will work with checked attribute (add/remove it from input tag) in <input type="checkbox" name="vehicle" value="Car" checked> ? (if we change class to checkbox in your code it does NOT work (I check this))Verbenia

© 2022 - 2024 — McMap. All rights reserved.