Reputation: 10745
Consider an Angular 2 component that has an @Input
meant to be a boolean, where its presence indicates true
and its absence indicates false
. Right now, I might manage this either with a custom getter/setter, or by having a function that tells me whether the attribute is present:
@Component({
selector:'foo',
template:`<div [class.error]="hasError()">Hello world</div>`
})
class Foo {
@Input() error:string;
public hasError() {
return error === '' || !!error;
}
}
And then I can use it in another component like this:
<foo></foo> <!-- no error class -->
<foo error></foo> <!-- has error class -->
What I really want to do is this, but have the same behavior:
@Component({
selector:'foo',
template:`<div [class.error]="error">Hello world</div>`
})
class Foo {
@Input() error:boolean;
}
Is there a common pattern for creating this behavior without the boilerplate in my first example?
Upvotes: 6
Views: 2317
Reputation: 10396
This works for me (Angular 8.2):
import { Component, OnInit, Input } from '@angular/core';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
styleUrls: ['./my-component.component.scss']
})
export class MyComponent implements OnInit {
@Input() inverse = false;
ngOnInit() {
this.inverse = !(this.inverse === false);
}
}
If you want more than a presence/absence check and really check for empty string, you can do that, too:
this.inverse = (this.inverse === "");
Upvotes: 0
Reputation: 2236
What you need is a decorator that wraps your boolean property as a getter/setter and handles all the logic.
It's quite simple and save that boilerplate.
This feature is already implemented by the material team in google, they build the material library for Angular 2 and they work closely with the angular team.
Currently it's implemented in their repo, not in the angular repo but if high demand will raise I suppose they might consider migrating it to angular, it has been done in some cases before.
Anyway, this is dead simple and it's around 15-20 LOC.
/**
* Annotation Factory that allows HTML style boolean attributes. For example,
* a field declared like this:
* @Directive({ selector: 'component' }) class MyComponent {
* @Input() @BooleanFieldValueFactory() myField: boolean;
* }
*
* You could set it up this way:
* <component myField>
* or:
* <component myField="">
*/
function booleanFieldValueFactory() {
return function booleanFieldValueMetadata(target: any, key: string): void {
const defaultValue = target[key];
const localKey = `__md_private_symbol_${key}`;
target[localKey] = defaultValue;
Object.defineProperty(target, key, {
get() { return (<any>this)[localKey]; },
set(value: boolean) {
(<any>this)[localKey] = value != null && `${value}` !== 'false';
}
});
};
}
export { booleanFieldValueFactory as BooleanFieldValue };
You can see the implementation in THIS LINK
Upvotes: 2