Learn Angular component design patterns from material components

What material components can teach us about component design patterns!

Bharath Ravi
Level Up Coding

--

“Make it simple, but significant.” — Don Draper

Angular is one of the most used frontend SPA frameworks out there. It is unique in many aspects. The developer community and the Angular team together have set best practices and good-to-haves that all of us follow. This together with the architectural choices makes Angular opinionated. But that is not necessarily a bad thing. These practices help to keep the codebase similar and easy to understand by others. The framework itself is bundled with tons of features that we as developers can leverage to create almost everything we need ⚔️

I personally, started learning Angular from youtube tutorials. I found amazing tutors on youtube that made concepts clearer than the minimal explanation we had on the documentation. I have been building applications from what I learned and I manage to get my business needs to meet 🙌

Being self-taught, online resources are the best source of knowledge. But unfortunately, even in most of the glamorous youtube channels and online tutors, best practices and design patterns are minimal; mostly forgotten.

So, where do you learn these from? The best way would be to look at someone else’s code which you think is designed well and adapt from. There are many libraries/plugins you can pick for this but, hey, the Angular team has its own component library, the Material component library! Where else can you learn design patterns better than the Angular team itself!? 🤷‍♂

Why Angular material?

Material components are set of angular components that are well tested and ready to use, built by the Angular team. These components are used by the community over the years and have it’s reputation high.

Apart from using them in our projects, there’s one other, maybe the more valuable use for these components. Material components can teach a lot about component design patterns in Angular.

We will be taking a shallow look at the Angular material library in the rest of this article and gather patterns that we can adapt to for our future projects!

For keeping this article simple, we will be looking at the source code of mat-button and mat-card.

Mat-Button

mat-button is probably the simplest material component we can inspect. It is a wrapper around native but <button> element.

mat-button documentation can be found here 👇

Attribute selector in a component selector

Material components evolved over the years of developments. One of the key takeaways here comes from the distinction between older versions of components and newer ones.

In the initial version of the material library, this is how you can use a simple button component.

And today, in modern material versions, this is how you can do the same

Do you see the difference?

This is an intentional shift angular team made version to version.
Basically, the custom components with custom names were replaced by attribute selectors with a component selector😮

Did that catch you by surprise? Let me explain.

It is possible to use an attribute selector in a component selector. Most of us were using attribute selectors only for directives. This approach here has very promising use cases indeed.

Before further explanations, let us have a look at how mat-button has implemented this approach.

Credits: Angular material repository

As you see above, mat-button has leveraged this strategy by making the component selector(button) with attributes selectors(mat-button, mat-raised-button, etc). There are a couple of benefits here.

Firstly, screen-reader is more familiar with the native <button></button> component. So, this will increase the familiarity and insists on the point that mat-button is simply a button with some extra capabilities, not something new introduced.

Secondly, when we introduce a custom element(say <mat-button>) we need to forward each native attribute(Eg: type, name, disabled, etc) to the new custom element. That can be a hell of a task.

So, if you are creating a native element wrapper, use the attribute selector in a component selector instead of introducing a new element to the user.

exportAs and host properties

exportAs and host are two less common properties you can pick from the above component decorator metadata. Let’s see each and how useful they are.

From documentation,

The exportAs property takes the name under which the component instance is exported in a template.

That just says that you can use exportAs to expose your component public API to your template.

In practice, imagine you are creating a modal component. You need to have a trigger button to trigger the opening of the modal. You can use exportAs property to expose your component to the template and allow any element on the page to trigger the opening of the modal 👇

Modal component
The modal component used in a template

In fact, you can find this approach in mat-menu implementation.

The host property is as well of great use.

The properties inside host property will get changed whenever their corresponding expression values get changed.

For example in mat-button,

host: {
'[attr.disabled]': 'disabled || null',
}

host property is used to make disabled state to toggles as the class property disabled changes.

Another use-case could be to simply add classes to the host element.

Like so,

host: {
'class': 'btn btn-primary',
}

That now is just waw!

Extra templating for extra power

Now, let’s take a look at the button.html file, which is the template for mat-button.

Credits: Angular material repository

Surprised?

It’s not just content projection(<ng-content></ng-content>) here. There’s more. If you’re wondering how this would render on the DOM,

mat-button rendered on DOM

That now is a cool way to introduce more templates to configure your custom component. mat-button used this to introduce the ripple effect and overlay to the component. You can configure this the way you want it.

Mat Card

From mat-card source found here 👇

Multiple content projections

This might be something you already know. Anyways, let’s see how mat-card used this feature to design a better component.

Content projection in Angular is the technique to project any content in a component. For example, in the mat-button implementation above, we have seen <ng-content></ng-content> used to grab any element in between <button></button> tag.

This is nice. But, what if you want to select specific elements that are passed from your custom component?

You can use the select property to do just that. like,

Credits: Angular material repository

In the above first line selects everything except <mat-card-footer> and places on top of the template. The second line selects <mat-card-footer> element and places it on the bottom of the template. This way, it can be made sure that <mat-card-footer> element is always placed on the bottom of the template no matter where the placement of the user placed it on the template!

This would mean that both the below code snippets will lead to the same effect on DOM.

mat-footer placed on the top
mat-footer placed on the bottom

Rendered on DOM 👇

The output of mat-footer(both the above cases)

ain’t it an excellent trick?

What we have above is nearly the tip of the iceberg. There’s a lot more you can learn from material components. I hope this article will inspire you to take a deeper look into Angular material source code and build better Angular components.

For more articles like this 👇

Find me on twitter here, happy hacking!

--

--