Syntax tricks for Angular HTML attribute bindings

What’s great about Angular is that the framework provides many different features out of the box. Some are well documented and widely known, while others are more obscure and less commonly used, though very useful.

For instance, there’s a ngClass directive to dynamically set classes to an element based on conditions:

In the above example, the button will have an active-btn CSS class if isActive is true, and a disabled-btn CSS class if isDisabled is true. That’s what the directive does.

But the thing is… We don’t need a directive to do that. I never use ngClass.

Instead, I do this:

It works the same way, and I’d argue that the syntax is slightly easier to understand. No directive needed.

On a different note, if you’ve ever tried to set the values of non-HTML attributes to an element, such as data-test for unit or end-to-end testing, you would find out that this syntax doesn’t work: [data-test]="value"

For these “unknown” HTML attributes, we need to use the following syntax: [attr.data-test]="value"

And then all is fine.

@let for local variables in Angular views

Angular 18.1 brought a new feature to our Angular templates: the ability to create local variables. Just like the new control-flow blocks @if, @for, and @switch, the new syntax uses the @ character with a new keyword: @let.

Here is an example:

The above code displays 21 as the value of the Angular expression. Why is this helpful? In many cases, we deal with complex nested objects that translate into long, verbose expressions:

With @let, we can make things shorter:

Another major use case is when using the async pipe. I covered several tricks on how to use the async pipe in the past, even introducing ngrxLet as an alternative, and all of these tricks can now be replaced with @let.

Here is the initial problem with async when dealing with data from an Observable that we need in several places:

The above syntax is not only horrible, but it also multiplies subscriptions to the source Observable, which is even worse. The common workaround consisted of using a structural directive because such directives allow using local variables as follows:

That’s more readable but still not ideal. Here is what we can do with @let now:

That’s a lot better. If the multiple ? drive you crazy, you can do this instead:

@let variables are read-only and cannot be reassigned. They behave like the const keyword in Javascript. The scope is also to the nearest block (which means view, in the case of Angular templates), so in my above example, addr is only available within the @if block, invisible outside of it.

You can play with my examples on Stackblitz here.

model() for signal-based 2-way data bindings

Angular 17.2 brought more features to the signal-based components approach, where almost everything in our components will become signals. We’ve already covered that inputs can be signals since Angular 17.1.

As an Angular developer, you’ve probably used ngModel in the past to create two-way data bindings. You might have created your own 2-way bindings using the approach covered in this tutorial, too.

All of that becomes even easier with Angular 17.2, as we can now do the following using the model function:

The above syntax means that we can pass a value to NameEntryComponent‘s age property as follows:

We can even have a 2-way binding (meaning something that acts as both an input and an output) by using the “banana in a box” syntax as follows:

This gets even better as we can specify that a value is required for that model input using model.required:

Since age and firstName are signals, we can update their values using set or update:

Where increaseAge is a method that uses an arrow function (as those aren’t supported in Angular templates):

You can see this example in action on Stackblitz here.

We can pass data to models using regular component properties:

But it gets even better as we can also use signals as-is with these 2-way bindings:

You can see this new example in action on Stackblitz here. Next week, I will cover 4 more functions available since Angular 17.2, all signal-based as well!

Angular 17: Simplifying style(s)

A major focus of the Angular team over the past two years has been to improve the developer experience by removing some of the common annoyances in the framework (better error messages, for instance). This is often referred to as “quality of life” improvements, and the Angular 17 feature I want to cover today is undoubtedly one of those.

Have you ever wondered why the component decorator always had a styleUrls or styles attribute even when 99.9999% of the time, we need one style URL or CSS style?

Well, I have good news for you! Angular 17 introduced a styleUrl attribute (singular) for a single style file:

And the other styles attribute has more flexibility as well as it now supports a single string or an array of strings, which means all options are available now:

A simple change that makes our life easier!

Dependency Injection: ElementRef

We use dependency injection daily to inject services in our Angular “objects,” such as components, directives, pipes, and other services.

Angular does offer other kinds of injectables, though, and one of those is the ElementRef. Its primary purpose is to give us direct access to the native DOM element of a component or a directive.

Here’s a code example (you can test it in Stackblitz here):

The above code changes the background color of the HTML element to red. It’s important to note that the above implementation is not ideal. We should use Angular data bindings for such a use case, which are a lot more readable (code example in Stackblitz here):

The Angular team doesn’t recommend using this API either:

So when should we use ElementRef? Just like markForCheck, it’s a last resort option, and we should use it when everything else fails. The only use case I would consider for ElementRef is when I have to interact with a non-Angular 3rd-party library, such as a chart or a map library, after carefully checking that Angular wrappers are not available on npm (or such wrappers do not pass my tests for good, reliable dependencies)

ChangeDetectorRef.markForCheck()

The following method call is either something you’re either very familiar with or something you’ve never heard about: ChangeDetectorRef.markForCheck()

If you’ve never heard about it, congratulations! It means you’ve been using Angular in a way that doesn’t require you to mess with change detection, and that’s an excellent thing.

On the other end of the spectrum, if you see that method in many places in your code, your architecture might have a problem. Why is that? As we covered before, there are two types of change detection: Default and onPush.

When we use onPush, we’re telling Angular: “This is a component that relies on @Input values to display its data, so don’t bother re-rendering that component when @Input do not change”.

The thing is, sometimes people start using onPush full of good intentions. Then, they start injecting services into their component, and when the data in those services change, the component does not re-render… So, instead of disabling ChangeDetection.OnPush (or figuring out a better way to keep their component as a presentation component), they go to StackOverflow, where people say: Just use ChangeDetectorRef.markForCheck(), and problem solved! Here’s a typical screenshot of such a response:

That’s because ChangeDetectorRef.markForCheck() does exactly that: It’s a way to tell Angular that something changed in our component, so Angular must check and re-render that component as soon as possible. As you can guess, this is meant to be an exception and not the rule.

The main reason why we would use ChangeDetectorRef.markForCheck() is if we integrate a non-Angular friendly 3rd party library that interacts with the DOM and changes values that Angular cannot see because Angular doesn’t manage that DOM. In other scenarios, we should avoid using it and think about a better architecture.

How to handle drag-and-drop with Angular?

Drag and drop features can dramatically improve the UX of a web application. Implementing drag and drop with Javascript seems intimidating, which is why we have the Angular CDK (Component Development Kit).

Let’s say we have a list of items we want to be able to reorder using drag and drop:

Thanks to the CDK drag-and-drop directives, such a task is easy. Here’s all the code needed to enable it:

The two directives cdkDropList and cdkDrag enable the drag-and-drop feature. We also need the following code in our component class:

The moveItemInArray function is a utility function from the CDK, so we don’t have to worry about that.

See the source code and try this example in action on Stackblitz here.

Increasing binding specificity

Let’s say we want to trigger an action when the user hits enter on their keyboard. We could do something like this, capturing the keydown event and then decide what to do based on the entered key:

While the above works, it’s pretty verbose. Instead, we can make our binding more specific by doing the following:

Now, that’s a lot more specific and less verbose. The same goes for class bindings. Here’s a lengthy example:

Since all we want to do is toggle the green class on or off based on a boolean expression, the following syntax is shorter and better:

You can see these examples in action on Stackblitz.

Angular 16.2: NgComponentOutlet input bindings

Another feature of Angular 16.2 is using input bindings with component outlets. You might not be familiar with ngComponentOutlet in the first place, so let’s explain what the directive does.

Let’s consider the following template syntax:

The above code would load a dynamic component into the ng-container. Assigning a new component type to componentTypeExpression would display it in that container. Here’s a basic example that shows a HelloComponent in the component outlet:

This can be helpful if you need to use different types of components and don’t want to (or can’t) use the router to make it happen.

The new feature of Angular 16.2 allows us to pass input values to that component. Let’s assume that HelloComponent looks like this:

We can bind a value to the name input as follows:

And that does the trick! You can see an example in action on Stackblitz here. inputs is an object that can have as many keys as needed. In my example, there is only one: name.

Using setters with @Input()

Following our theme of lifecycle methods with ngOnChanges and ngOnInit, I want to give you another interesting trick to be notified when an @Input value changes.

We’re already familiar with that syntax:

But what if instead of applying @Input on a class property, we used it on a setter:

The input works the same as before, with the advantage of running several instructions when a new value is set. All of that without using ngOnChanges. This approach is practical if you have several side-effects to trigger depending on which input changes, which would be tedious to handle with multiple conditionals:

The setter approach brings more clarity and leaves less room for mistakes: