All about environment variables with Angular

Angular apps created with the Angular CLI used to have automatic environment creation built-in for new projects. While this automation was removed in recent years to simplify the learning curve of Angular, it is still possible to add support for different environments using the command:

ng generate environments

Before we generate such environments, it’s important to take a look at angular.json in the root folder of any Angular project, more specifically, the section called configurations in the build section:

This tells us that our project has two different configurations: production and development, and the default config for the ng build command is production.

Note that the serve section of angular.json has a similar config that extends the build targets and overrides the default configuration by making it development, which, as a result, is what we use when we run ng serve:

If we want to override the default configurations when running ng serve or ng build, we can do this by adding a configuration flag to the command and making it point to the proper configuration name, for instance:

ng serve --configuration=production

ng build --configuration=development

How do we plug environment variables into this?

When we run ng generate environments, the Angular CLI does two things:

It creates two new files in src/environments: environments.ts (the environment for production, which is also the default) and environment.development.ts for development.

angular.json gets updated with the following highlighted syntax:

As you can see, this instructs the builder to replace the production environment file with the development file when we use the --configuration=development flag.

Such environment files can contain any environment variables we want. For instance, say I want to define a SERVER_URL that is different for production and development. I can do that:

Now, to use such environment variable in my code, all I need to do is import it as follows:

Important: The import has to be from /environments/environment. Angular will pick the correct configuration based on the flag given to the build or serve command.

How do you create environments for QA, pre-prod, and more?

Now that we know that angular.json is the file where all this magic happens, if we want to create a qa environment, we can head to the configurations section of build, add a new configuration block with the name q (or anything you want, of course), and make the file replacement to a new file that I call environment.qa.ts:

Then, I can create that environment file under src/environments and add any variables I need in it:

And that’s it! We covered how to create environment variables for production and development and how to create other custom environments we might need.

5 tips for Strictly Typed Reactive Forms

Forms can be a very complex part of any web application, and today, I will cover some useful tips and tricks for using Reactive Forms with strict types.

  1. Use nonNullable: true to restore default values on reset

Form controls and groups are nullable by default in Angular, which means that the following example of FormControl value isn’t of type string:

Instead, if you try to read email.value, you’ll see that the type is string or null. Why would that be the case since we have a default value? That’s because the form can be reset with a <button type="reset"> on the form or by calling email.reset(), for instance.

The trick then is to make that control non nullable by adding the following option:

Now, when the form gets reset, email.value is equal to "test2@gmail.com", which is perfect in several different form scenarios, such as an edit form, where we don’t want to “lose” the previous values, but just “reset” to those values.

2. Using non-nullable form controls at scale

If we want to apply that same config to several form controls, we can put them in a FormGroup and pass that same nonNullable: true option to the FormGroup constructor instead of specifying it for every single control. But what if we use the FormBuilder service?

Then we can use the following syntax:

3. How to define the type of a FormControl with no default value?

If we do something like this:

We’re in trouble because Typescript will infer the type of password.value is null. Instead, we use generics and a union type to specify the expected type for that value, here string or null:

4. How to read the values of disabled form controls?

In complex forms, it is very common that some controls are enabled/disabled based on some condition. For instance, if the user selects the country “USA, ” we might enable a state dropdown with the US states in it and disable it for other countries.

When a control is disabled, its value doesn’t appear as part of the form group value. Here is an example where age is obviously equal to 21, but since that control is disabled, formGroup.value doesn’t return it:

The workaround here is to use formGroup.getRawValue(), which will return everything, including disabled control values:

You can see that example in action on Stackblitz here.

5. How to handle dynamic forms where we don’t know which controls will be present ahead of time?

In some cases, forms can be completely different depending on the type of user, country, etc. As a result, we don’t know whether a control will be present or not at runtime. We can define that uncertainty in Typescript using the optional operator ? when we define the type of our form:

In the above code, password is defined as optional in our interface, meaning that the compiler will not throw an error if we remove that control later.

If your form is 100% dynamic and handling proper types for each scenario would be a nightmare, you can use the more flexible FormRecord instead of FormGroup:

What’s new in Angular 17.1?

Angular 17.1 brings new features to the framework and goes one step further towards signal-based components with the introduction (as a developer preview) of Signal inputs.

What’s a Signal input?

Instead of using the @Input decorator, we can now use the input() function from @angular/core. This function creates an input value that is received as a signal. Here is an example:

Since name is a signal, it can be read like so:

There are several benefits to that approach. For instance, you can derive a value from that input without using ngOnInit or ngOnChanges. Instead, we can use computed:

You can see the above code in action on Stackblitz.

The new API also supports required inputs with the input.required function to ensure a value is passed to the component/directive:

Router

We can now pass information to a route using the info option as follows:

Or using the router service:

This info object is not persisted in the browser session history, unlike the state option, which can also be used to pass data to a route.

Application builder migration

If you still need to migrate to the new Vite-based application builder, you can do so with one simple CLI command now:

ng update @angular/cli --name=use-application-builderCode language: CSS (css)

Unit tests without a browser

Another exciting update is an addition to the ng test command so you can run your unit tests without opening a browser, which is perfect for continuous integration, for instance:

ng test --no-browsers

These are the most essential changes in Angular 17.1. I’d say it’s a pretty good release!

Need your input + some goodies

Welcome to the first edition of the Weekly Angular Newsletter! With this new weekly format comes the option to do things differently, such as more in-depth tutorials, code challenges, or audio/video content.

As a result, I would like to collect your input by filling out this form. That way, I’ll know what content works best for you, which topics you want me to cover, and in which format. I pre-selected a few options in the form, but feel free to submit completely different ideas if needed. Thanks a lot!

Free conference tickets, anyone?

I’ll speak at DeveloperWeek 2024 soon, in-person in the San Francisco Bay Area and Online a week later. The organizers allowed me to share free tickets with you so you can attend the conference in person or remotely. You can register here for free.

DeveloperWeek is a generalist developer conference with multiple tracks and topics: AI and machine learning, web, Dev Ops, APIs and micro-services, and more. My talk will be about Cypress, and you can also register for my free online workshop for similar content.

Finally, I’m also speaking at ng-conf 2024 this year! I have ONE $225 discount code to give away, and the first person to reply to this email will get the code so they can enjoy a discounted ticket to the best Angular conference in the world!

My talk at ng-conf will be a 2-hour workshop on server-side rendering with Angular! I hope to see many of you there. In the meantime, don’t forget to give me your input, claim a free conference ticket, and get ready for next week’s post!

Ideas to improve your Angular skills in 2024

The number one impediment I’ve seen over the years for Angular developers to grow with the framework is that they’re stuck with an older version of Angular at work and can’t use any of the modern features of Angular 16 and 17, for instance.

This tends to happen because of dependencies, which makes this newsletter entry on picking your dependencies one of the most critical pieces of advice of the year. As a result, this would be the number 1 skill I’d recommend for an Angular team lead or solo coder because it will allow you to upgrade often and never stay out of touch with the framework.

On a side note, being (or becoming) the developer who takes the initiative to create a branch and upgrade Angular to a newer version will make you indispensable at work, which is the best thing for career advancement.

Another idea, if your work project is up-to-date, is to start introducing Signals or the new control flow syntaxes. This will achieve the same result as a complex upgrade: Your colleagues will be more reliant on you for all things Angular, which elevates your status in the company.

If your current work project seems helpless, challenge yourself with a side project, which could be one of my Angular certifications or even the Angular Accelerator program, where you’ll build a brand new app with Angular 17 through challenging yet realistic exercises.

So that’s what I would recommend:

1) Take charge at work and try to upgrade existing apps (or introduce modern features such as Signals into them if you’re already up-to-date)

2) If achieving any of this in your work projects seems impossible, try to do so by working on open-source projects, certifications, or a series of exercises similar to the Accelerator program.

Angular 16 and 17 top features

This year, Angular has had two major versions with really major changes. This post highlights the top three features of Angular 16 and 17 worth getting into before we start 2024, with links to all relevant newsletter entries so far so you can revisit and explore those:

  1. Angular Signals (dev preview in 16, officially released in 17)

Signals are super important for performance; they make Angular easier to learn (compared to RxJs), and they’re just a cool feature to get into!

2. defer() block for lazy-loading (dev preview in 17)

Lazy-loading is one of the main Angular features to improve app performance and defer() is a brand new tool we have available to customize lazy-loading in all sorts of creative ways.

3. New control flow syntaxes (dev preview in 17)

If the ngIf - else syntax drives you crazy. You’ll love the new block-based control flow syntax. It’s convenient, easy to read, and does not require any dependencies, which is better for performance and simplifies the creation of standalone components (no imports needed!):

You can also check out this post with short video recaps of Angular 17 features and my full recap of Angular 16 features.

Most popular posts of 2023

Happy holidays! As we get closer to the end of the year, this week will be all about recaps from what we covered in this newsletter in 2023. That way, you can revisit important topics covered so far as we prepare for 2024.

Also, in 2024, the newsletter will start a weekly cadence instead of daily, so you’ll still get Angular content for free, but once a week instead of every workday.

Most popular post: Three ways to update Angular Signals.

Angular 17 removed one of these three methods to simplify the Signals API. Only set() and update() are left at this point. I also have a super cheap Signals course if you want to learn more during the holidays.

Second place: Using a loading template with ngrxLet.

Loading animations and skeleton loaders are always popular topics, probably because most web apps use such features to some extent.

Third place: RxJs withLatestFrom operator.

We covered many RxJs operators this year, and withLatestFrom was the most popular one. If you’re looking for more, shareReplay and forkJoin are some good recommendations.

Angular Component Testing with Cypress

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!

The date is January 18th, 2024, at 9 am US/Pacific (time zone converter here).

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.

Animated Route Transitions with Angular 17

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:

And… that’s it! Router animations are now in place. You can see the code for that example in action on Stackblitz.

Dynamic forms with FormArray

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:

You can see that code in action on Stackblitz here. You can read this excellent guest post from Jennifer Wadella on my blog for more information on dynamic forms.