Angular Component Design Anti-Patterns

Austin
2 min readFeb 10, 2018

--

As component developers of Angular, our consumers expect Angular components to be reactive in nature. I’m going to talk about a anti-pattern that is a common mistake of component developers around inputs.

In traditional jQuery widgets it was very common to have a master options object and as we start developing components in Angular we should avoid this pattern. For example:


@Component({

})
export class MyComponent {
@Input() options = {
resizable: true,
draggable: true
};
}

In this component, we have a options input that declares the options and their defaults for this component. In order for people to consume this plugin, its pretty much necessary for the consumer to have to declare the component in the template and then have a options object in the consumer component class like so:


@Component({
template: `
<my-component [options]=”options”></my-component>
`
})
export class MyConsumer {
options = {
resizable: true,
draggable: true
};
}

Angular really shines in its declarative syntax, this is forcing us to do it like the jQuery way. Additionally, when we use global option objects like this it makes it hard to support two-way binding and have a intuitive API. In order to update your options, you typically find people make these methods to set options like so:


@Component({
template: `
<my-component #mycomp [options]=”options”></my-component>
`
})
export class MyConsumer {
@ViewChild(‘mycomp’) mycomp;
updateResizable() {
mycomp.setOption(‘resizable’, true).refresh();
}
}

Angular developers expect when they change something for it just to update and as component designers we need to consider this.

Lets look at a different approach to solving this; rather than having a global options object we declare individual inputs for each option. This will allow us to:

- Use better change detection strategies such as OnPush
- Make our component more reactive
- Support two-way binding syntax

If I were to rewrite the above with this in mind, it might look something like this:


@Component({

})
export class MyComponent {
@Input()
set resizable(val: boolean) {
this._resizable = val;
this.refresh();
}
get resizable(): boolean { return this._resizable; }
private _resizable: boolean = true;

}

While this is a bit more verbose, it allows use to use getters/setters to react to property changes automatically without having to call things like setOptions and refresh at the consumer level.

--

--

Austin

Crafter of Software • Lover of #JavaScript #goldendoodles & #opensource