Angular Component Design Anti-Patterns

Austin
Austin
Feb 10, 2018 · 2 min read
Image for post
Image for post

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.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch

Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore

Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store