Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make Angular 2 component input "true" when the attribute is present and empty

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?

like image 246
Ben Dilts Avatar asked Jul 19 '16 15:07

Ben Dilts


1 Answers

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

like image 159
Shlomi Assaf Avatar answered Oct 25 '22 21:10

Shlomi Assaf