Angular Tips: Template Binding with Static Types
In Angular, we have the availability to use TypeScript and editor tools that can statically analyze our components. Editors can provide autocompletion feedback and can detect typos in our templates. In this post, we will cover a couple of tricks that can help prevent pesky typos in our HTML templates and catch errors at build time rather than run time.
A frequent use case is toggling the visibility or CSS class of a DOM element. Usually, we toggle this information on some type or state. For example, in our app, we have different notification messages we want to display to the user such as an error or success message.
In our example, we will toggle the visibility and styles of the element based on the type. In our use case, we have a message component to display messages to users in various states. Our message states are the following: Success, Error, Warning, and Default.
Static Analysis
Our message component has two inputs. First, it takes the message type, example error vs. success. The second input is included via ng-content is the actual message we would like to display. Let's take a look at the message component below.
import { Component, Input, OnInit } from '@angular/core';
export enum MessageTypes {
Default,
Success,
Error,
Warning
}
@Component({
selector: 'app-message',
template: `
<div
class="message"
[ngClass]="{
'message--error': messageTypes.Error === type,
'message--success': messageTypes.Success === type,
'message--warning': messageTypes.Warning === type
}"
>
<ng-content></ng-content>
</div>
`
})
export class MessageComponent implements OnInit {
@Input() type = MessageTypes.Default;
messageTypes = MessageTypes;
}
Our component has the one input for the type of message to display and then displays the content with the ng-content
feature. Ng content takes whatever content we place in the component tags and inserts it into our template. We will see this in just a moment.
The important thing to notice in our component above is our enum we are using for the message types. We can bind the enum to our component property.
export enum MessageTypes {
Default,
Success,
Error,
Warning
}
What does this give us? We can now reference a property in our templates instead of magic strings.
<div
class="message"
[ngClass]="{
'message--error': messageTypes.Error === type,
'message--success': messageTypes.Success === type,
'message--warning': messageTypes.Warning === type
}"
>
<ng-content></ng-content>
</div>
Now if refactor our code there or no magic string duplicated across our code base, and we can rename and refactor as needed.
Editor Autocompletion
This technique also makes it easier to use our components and understand the API signatures. Below is my component that is using the app-message
component.
import { Component } from '@angular/core';
import { MessageTypes } from './message.component';
@Component({
selector: 'demo-app',
template: `
<app-message [type]="messageTypes.Error">
Error: Hello World
</app-message>
`
})
export class AppComponent {
messageTypes = MessageTypes;
constructor() {}
ngOnInit() {}
}
We can see we bind the message type enum to our demo component. Now in our templates we will get static typing, and our editor can provide autocompletion like below.
Taking advantage of TypeScript and Angular template syntax we can help lower the chances of production run time errors and catch them early in development. Check out the working demo below. To see the template static type checking in action copy the demo into an editor with TypeScript support like Visual Studio Code.