54

I'm trying to write a component to be reused across my application, which will sometimes show a control button and sometimes not.

Ideally, I'd want to get this from the presence or absence of an attribute, so that using the component would look something like <generic-component hideControls></generic-component>, with a boolean variable in my component based on whether that attribute is present, but I can't see a way to do this.

Is there a way to do this?

I have tried with the slightly more messy version below (although ideally I would not need to set a value on showControls/hideControls);

generic.component.html

<div>
  <div>Stuff</div>
  <div *ngIf="showControls">Controls</div>
</div>

generic.component.ts

// imports etc.
@Component({
  selector: 'generic-selector',
  templateUrl: 'generic.component.html'
})
export class GenericComponent implements OnInit {
  @Input()
  public showControls: boolean = true;

  // other inputs and logic
}

This fails, because the usage <generic-selector showControls="false"></generic-selector> sets showControls as the string "false", which is truthy, rather than as the boolean value. So I have to get round it by adding a lot of mess to the component to take in the input and convert to a boolean based on whether it is being given the string "false" or not. A non-messy way to sort this would be appreciated, but I'd prefer being able to do the other option above.

4 Answers 4

60

This fails, because the usage <generic-selector showControls="false"></generic-selector> sets showControls as the string "false", which is truthy, rather than as the boolean value. So I have to get round it by adding a lot of mess to the component to take in the input and convert to a boolean based on whether it is being given the string "false" or not. A non-messy way to sort this would be appreciated, but I'd prefer being able to do the other option above.

using binding

<generic-selector [showControls]="false"></generic-selector>
Sign up to request clarification or add additional context in comments.

4 Comments

[showControls]="false" works well. showControls={{false}} does NOT work.
Learned more about these bracket-attributes here stackoverflow.com/questions/35944749/…
solution working perfectly but why is angular not considering false as variable someone please explain...
@AbhijitJagtap false is a reserved keyword in JavaScript and can't be used as a variable name.
39

You can use the Input decorator as you would normally do with other attributes. The only trick is that when the attribute is non-present, the value would be undefined, otherwise, the value would be an empty string.

Logic:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: '',
  templateUrl: './boolean-component.component.html'
})
export class AppBooleanComponent implements OnInit {

  @Input('boolean-attribute') booleanAttr: boolean;

  ngOnInit() {
    this.booleanAttr = this.booleanAttr !== undefined;
    console.log(`Boolean attribute is ${this.booleanAttr ? '' : 'non-'}present!`);
  }

}

Template 1 (logs 'Boolean attribute is present!'):

<app-boolean-component boolean-attribute></app-boolean-component>

Template 2 (logs 'Boolean attribute is non-present!'):

<app-boolean-component></app-boolean-component>

Comments

17

In Angular there are two ways to assign a value to your property:

  1. using as HTML attribute
  2. with square brackets

In the first case, the value of your property will always be a string, like every HTML attribute even if you'll assign an interpolable string like this: {{true}}.

While in the second case, it'll be interpreted as a JavaScript expression but in your context. So if you'll have an object notation there the value of your attribute will be the parsed as an object. But for specifying context angular is not using "with" statement so you can't use your globals there, but only the properties of the component in which scope your component is inserted.

<generic-selector [showControls]="false"></generic-selector>

In this case, it takes the string "false" and translates it to JavaScript. So in JavaScript, it's a boolean, then you'll get it as a boolean.

But if you'll have something like this: <generic-selector [showControls]="{myProp: 'val'}"></generic-selector>

then the type of your showControls will be object and its value will have myProp property equal to 'val'.

but if you'll have some nonliteral there then it'll be considered to be a property of your class:

<generic-selector [showControls]="location"></generic-selector>

then if you'll have a location property on the scope of the component containing your generic-selector then the value of its location property will be taken otherwise it'll be undefined.

I'll suggest you to consider using ngOnInit for logging the property's type and the value assigned.

Comments

4

With Angular 16.1 or later you can use the transform option with the booleanAttribute function on the @Input() and let Angular handle the transformation of the input to a boolean:

import {Component, Input, booleanAttribute} from '@angular/core';

@Component({
  selector: 'generic-selector',
  templateUrl: 'generic.component.html'
})
export class GenericComponent implements OnInit {
  @Input({transform: booleanAttribute}) showControls = false;

  // other inputs and logic
}

For more details see the pull request which added the feature.

1 Comment

Added notes and demo to this article: fireflysemantics.medium.com/…

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.