Great news! Thanks to all your contributions, I’m happy to organize another coffee-fueled 2-hour workshop in January, which will be about testing Angular components with Cypress!
Like last time, the workshop will be on Zoom, and we’ll have a few hands-on exercises. A recording will be shared with all attendees after the workshop. You can register here and contribute here to keep me caffeinated.
If you have ideas for future workshops, please let me know. I do have a few, but I’m always open to suggestions.
Yesterday, we covered how to generate CSS animations for our Angular applications. Today, let’s see how to use such animations as route transitions when navigating from one route to the next:
The first thing we need to do is add the right providers for animations and transitions along with our routing config:
Then, we can create two different animations: One to exit the current “page” and one to enter the next “page.” In my example, I made two animations called entrance and exit using the CSS animation generator tool:
Angular has two specific selectors for view transitions: ::view-transition-old(root) for the exit of the old page and ::view-transition-new(root) for the entrance of the new page. Our next step is to “plug” our animations into these selectors:
The end of the year is getting closer and closer, and I realized I haven’t covered anything CSS-related in this newsletter yet! Let’s fix this today with a tool that makes generating CSS animations super easy: CSS generator.
As you can see in the animation, you only need to select a type of animation, tweak some parameters, and then copy/paste the provided CSS code into your app. This example shows you how I used that tool to animate all my divs:
I created a quick example in Stackblitz to see that code in an Angular app. Note that CSS generator can be used for other CSS purposes, such as tweaking border-radius options, opacity, sepia filters, and more. It’s a great way to discover new CSS features and config options since CSS is probably the web language that has evolved the most over the past ten years.
As an Angular coach, I review a lot of Angular applications. One thing that I see in a lot of code bases is the use of Typescript enums to store a bunch of constants:
The above code creates a new type CompassDirection with four possible values: CompassDirection.North, CompassDirection.East, etc.
Each constant gets assigned a numerical value starting at 0, so in that example, CompassDirection.North is equal to 0, and CompassDirection.North is equal to 3.
These values can be customized, and we can use strings, objects, or anything we want instead of numbers:
I avoid enums because they’re neither convenient nor performant. For instance, this is what the last example gets compiled into by the Typescript compiler:
That’s not pretty, and it comes at the extra cost of all that code being downloaded in the browser and then interpreted, which impacts performance.
Also, enums aren’t very convenient to be used in component templates. If I want to use an enum in a component template, I need this additional code to make that type accessible on the component instance (the this reference):
We still get a type associated with specific values, but now this gets compiled into the following:
No, I didn’t forget anything in that black rectangle: Union types are like interfaces and don’t get compiled into anything, meaning they do not increase the size of your code base or impact performance. Also, since those types are just the union of other types (such as strings or numbers), these constants can be used as-is in a component template without needing to tweak our component class.
In other words, union types preserve type safety without degrading our app’s performance, which is a win-win.
We’ve covered reactive and template-driven forms in the past. One of the nice features of reactive forms is the FormArray class. You can use FormArray for a form where the number of inputs is dynamic and can increase or decrease:
To achieve the above, we create a FormArray in our component and add a method so we can append new FormControls to that array:
Then, we use a @for block to display all controls of that array in our template, including a button to add new names to the list. Note that we store the index of each control in a local variable i:
We can even add support for the removal of form elements using the removeAt method of FormArray and the index of the element to remove:
And that’s it! We can check that the FormArray value is updated correctly with the following expression:
A few months back, I shared a cheat sheet for Typescript Control Flow. Today, I want to share a cheat sheet on types, which are the main reason why Typescript is so useful.
A few interesting features that aren’t too well known:
The keyof operator and the Type[Property] illustrated in the Mapped Types section
One of my favorites: Union types and the even more powerful Template Union Types (bottom right corner of the cheat sheet)
We’ve covered how to use @defer to lazy-load blocks of code in our Angular v17+ applications. We also touched on the different trigger options as well as the ability to create custom triggers with when.
The above code would display my-component when the user interacts with the web page, but it would prefetch the code on idle, meaning as soon as the browser isn’t busy doing anything else. That way, when the user starts interacting with the page, the component has already been downloaded (or is currently downloading) from the server, which speeds things up.
The nice thing about prefetch is that it supports the same triggers (idle, viewport, interaction, hover, immediate, timer) as the @defer block, which allows for lots of different possible customizations.
I’m including links to previous newsletter entries for deeper dives on the different topics involved in that solution. And there are quite a few subtleties to overcome.
In our component class, I stored the Observable from our service instead of storing the data. No more constructor needed:
You can see that solution in action on Stackblitz here. We fixed our code and added some performance improvements as well by ensuring we can’t get into memory leaks and optimizing change detection for that component.
One of the cool things about teaching Angular, running an Angular certification program, and interacting with hundreds of developers (and code bases) all around the world is that I get to see a lot of good ideas but also a lot of repeated mistakes.
Today, I want to share a small code challenge inspired by mistakes seen over the past few weeks so you can learn from these examples and hopefully won’t make these same mistakes in the future.
Here is the code I want you to look at:
You can run it on Stackblitz here and fork the project to debug what’s wrong and make it work. Currently, this App component displays nothing at all, which is a problem.
I’ll give you a possible solution tomorrow. In the meantime, feel free to send me your solution if you want.
It’s official! I’m running an Angular Signals Workshop next Thursday, December 14th, at 9 am US/Pacific time (12 pm US/Eastern – 6 pm Central European time). Registration and attendance are free by using this link, but your support is appreciated by buying me a coffee if you feel so inclined.
Of course, the more support I get, the more likely I am to organize similar events in the future. Just saying 😉
This workshop will be limited to 100 participants, so register as soon as possible to guarantee your spot. I’ll cover everything you need to know about Signals, starting from scratch and covering every single available Signal API in Angular 17. You’ll be able to ask questions, practice with a few exercises, and see me explain my solutions.
As a bonus, all exercises and examples will use recent Angular features such as the new control flow syntax, standalone components, the inject function, and more! That way, we’ll cover more than just Signals in the 2 hours we spend together that Thursday.
Here is the registration link again. Looking forward to seeing you there, and feel free to let me know if you have any questions before registering.