What's new in Angular 14?
Tags: angular, webdev, news
Published: 2022-08-26 23:02:56

Discover what changed!

{% embed https://youtu.be/DTWrJ_cC2s0 %}
## Introduction

Angular 14 was recently released with a bunch of exciting features. And today, I'll present them to you. They are:

1. Standalone components
2. Route Providers
3. ENVIRONMENT_INITIALIZER
4. Typed Forms
5. The `inject()` Function
6. Setting the Page title from the route
7. Autocomplete in the Angular CLI
8. Optional injectors in Embedded Views

So let's get started!

## Standalone components

For most people, the most significant change in this version is the possibility of creating components _without_ `@NgModule`s! Yeah, you got that right.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8fxtu1knvb89v43wv30a.gif) 

### Before Angular 14

If you are a bit lost, let me show you a folder structure from a classic Angular component:

```tsx
home 
| --home.component.html
|--home.component.css
|--home.component.ts
|--home.module.ts;
```

There, we have an `.html` file for the template, a `.css` file for the styles, a `.ts` file for the component, and another `.ts` file for the `@NgModule`. This last file imports the dependencies of our component, declares the component, and can also define some providers.

### A new possibility with Angular 14

In Angular 14, we can do all that directly in the component, without needing an `@NgModule`.

```tsx
home 
|--home.component.html 
|--home.component.css 
|--home.component.ts;
```

To do that, we just need to set the `standalone` property in our component to `true`.

```tsx
@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.scss'],
  standalone: true,
})
```

### Be aware!

Be aware:

1. Standalone components are not _the new way_ of declaring components! They are _another_ way of declaring components. The classic way of defining `@NgModule`s will not be deprecated.

> Well, I can't really promise that it won't be deprecated. But yeah, so far, there are no intentions of deprecating `@NgModule`s.

As a matter of fact. I will still use `@NgModule`s instead of standalone components because I like the isolation that `@NgModule`s provides.

>

1. Don’t migrate your entire application to standalone components yet!
   Standalone components are very recent, and it will take us a while to create conventions and define best practices. I recommend waiting a bit longer before jumping ship.

## Route Providers

But hey, if we drop the `@NgModule` and use standalone components, how can we set provider by route, like we used to do with `@NgModule`s?

To address that, Angular added route providers. So, basically, route definitions now have a property called `providers`. Allowing us to provide values only to the modules and components rendered by that route.

```tsx
const NAME = new InjectionToken<string>('token');

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    providers: [
      {
        provide: NAME,
        useValue: 'foo'
      }
    ]
  }
];
```

## **ENVIRONMENT_INITIALIZER**

Another thing we used to do with `NgModule`s was run a setup script when a lazy loaded module was initialized. In other words, the `constructor` of a lazy loaded module would only run when the module was initialized, and some developers take advantage of that to run some kind of setup.

```tsx
export class HomeModule {
  constructor() {
    console.log('You can run something here');
  }
}
```

How can we do that with standalone components? The solution is to use the `ENVIRONMENT_INITIALIZER` token.

```tsx
const routes: Routes = [
  {
    path: '',
    pathMatch: 'full',
	  component: HomeComponent
    providers:[
      {
        provide: ENVIRONMENT_INITIALIZER,
        multi: true,
        useValue: () => console.log("You can run something right here too")
      }
    ]
  }
]
```

This token allows us to provide a setup function that will run before the environment is initialized.

If we want it to run when we navigate to a route, we can provide this token using route providers.

This has the same effect as our previous solution using `NgModule` `constructor`s and has the added benefit of being more explicit.

## **Typed Forms**

Another long-awaited feature of Angular 14 is typed forms.

Before version 14, form values were typed as `any`. This means that we lose all the awesome type-safety of TypeScript.

```tsx
  const control = new FormControl(""),
	control.value
	//=> control.value: any
```

> By the way, there is a type-safe way of saying that you don't know the type of something. If you're interested in that, check out this one minute video explaining the differences between `any` and `unknown`.

Anyway, we don't have that problem anymore. Because Angular 14 has strict types for forms. So if a `FormControl` deals with a `string`, the value will be typed as a `string` instead of `any`.

```tsx
const control = new FormControl(""),
control.value
//=> control.value: string | null
```

## The `inject()` Function

Now, this is the most interesting feature to me.

Angular 14 introduces the `inject()` function. And it looks very much like React hooks. It gives us a universe of possibilities to reuse our code, the most relevant being that we can now create reusable functions which use dependency injection internally.

```tsx
export function getPosts() {
  const http = inject(HttpClient);
  return http.get('/api/getPosts');
}

export class HomeComponent {
  readonly posts = getPosts();
}
```

If you're as interested n functional programming as I am, you know that means a lot! But as Uncle Ben has once said:

> "_With great power comes great responsibility”_

This creates implicit dependencies in your functions. I expect my function dependencies to be explicitly declared in the function arguments. So if I see a function with no arguments, I imagine it to have no dependencies. But now, we can create functions that seem pure but inject a lot of dependencies internally, making them harder to test and more coupled to the Angular framework.

A good solution is to follow the same convention that we have on React, which is to prefix those functions with `"use"`. So that way, you can easily identify functions that use `inject()` internally.

```tsx
export function useGetPosts() {
  const http = inject(HttpClient);
  return http.get('/api/getPosts');
}

export class HomeComponent {
  readonly posts = useGetPosts();
}
```

## Set Page title from the route

One common task in web applications is changing the page title on each route.

In Angular, this used to be a very manual process, but now, we can simply define the page title in the route definition.

```tsx
export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    title: 'Home',
    pathMatch: 'full'
  }
];
```

But I know what you're thinking:

> "Lucas, I can't set the title in the route definition because my title is not static, it depends on something I get from my API".

Don't worry. Angular 14 got you covered.

If you need to customize the title dynamically, you can do so by extending the `TitleStrategy` class from the `@angular/router`, and providing your new, custom title strategy instead of the default one.

```tsx
@Injectable({ providedIn: 'root' })
export class PageTitleStrategy extends TitleStrategy {
  constructor(@Inject(Title) private title: Title) {
    super();
  }

  override updateTitle(routerState: RouterStateSnapshot) {
    const title = this.buildTitle(routerState);
    if (title !== undefined) {
      this.title.setTitle(`CompanyName | ${title}`);
    } else {
      this.title.setTitle(`CompanyName | Page without title`);
    }
  }
}

export const routes: Routes = [
  {
    path: '',
    component: HomeComponent,
    title: 'Home',
    pathMatch: 'full'
  }
];

bootstrapApplication(AppComponent, {
  providers: [
    importProvidersFrom(RouterModule.forRoot(routes)),
    { provide: TitleStrategy, useClass: PageTitleStrategy }
  ]
});
```

## **Angular CLI Autocomplete**

Another nice little thing is that we now have autocomplete in the Angular CLI.

Just type `ng` on your terminal and press `TAB` to let the Angular CLI either autocomplete your command or give you some help.

```tsx
> ng se
			//=> If you press TAB here, the CLI will complete

> ng serve
```

```tsx
> ng
		//=> If you press TAB here, the CLI will show a list of commands

> ng
	add           -- Adds support for an external library to your project.
	analytics     -- Configures the gathering of Angular CLI usage metrics.
	build         -- Compiles an Angular application or library into an outpu...
	cache         -- Configure persistent disk cache and retrieve cache sta...
	...
```

### Activating the CLI autocomplete

To activate it, you must run the `ng completion` command and confirm that you want to enable the autocomplete by typing `Yes`.

```bash
> ng completion

? Would you like to enable autocompletion? This will set up your terminal so pressing
TAB while typing Angular CLI commands will show possible options and autocomplete arguments.
(Enabling autocompletion will modify configuration files in your home directory.)

> Yes
```

Finally, just restart your terminal to have autocomplete enabled.

## Optional injectors in Embedded Views

Last but not least, we can now pass optional injectors in embedded views. In the past, we had to use other APIs to do that, such as the `ngComponentOutlet`.

```tsx
@Directive({ selector: '[foo]' })
export class FooDirective implements OnInit {
  constructor(
    private vcr: ViewContainerRef,
    private templateRef: TemplateRef<unknown>
  ) {}

  ngOnInit(): void {
    this.vcr.createEmbeddedView(
      this.templateRef,
      {}, // context
      {
        injector: Injector.create({
          // pass an injector :)
          providers: [
            {
              provide: 'foo',
              useValue: 'bar'
            }
          ]
        })
      }
    );
  }
}
```

## Don't Stop Here

These were the most relevant changes in Angular 14, in my opinion. If you want to see all changes, you can do so by checking out the references in the description.

If you want to dive deeper into the Angular framework, consider [subscribing to our newsletter](https://www.lucaspaganini.com/newsletter) at lucaspaganini.com. It's spam-free. We keep the emails few and valuable.

And if your company is looking for remote web developers, consider contacting my team and me. You can do so at lucaspaganini.com.

As always, references are in the description. Like. Subscribe. Have a great day. And I will see you in the next one.

– Lucas

## References

1. _[Angular 14 - Academind on Youtube](https://www.youtube.com/watch?v=ew6ljePlSSc&t=48s)_
2. _[O que há de novo no revolucionário Angular 14 - Andrew Rosário on Medium](https://andrewrosario.medium.com/o-que-h%C3%A1-de-novo-no-revolucion%C3%A1rio-angular-14-9741c9545a24)_
3. [Novidades no Angular v14 - Vida Fullstack](https://vidafullstack.com.br/angular/novidades-no-angular-v14/)
4. [What is new in Angular 14? - Nevzatopcu on Medium](https://nevzatopcu.medium.com/?source=post_page-----d31edf91fd3e--------------------------------)
5. [Angular v14 is now available! - Angular Blog on Medium](https://blog.angular.io/angular-v14-is-now-available-391a6db736af)
6. [What’s New in Angular v14 - Netanel Basal](https://netbasal.com/whats-new-in-angular-v14-df1b0c5d5e2f)
7. [Unleash the Power of DI Functions in Angular - Netanel Basal](https://netbasal.com/unleash-the-power-of-di-functions-in-angular-2eb9f2697d66)
8. [Getting to know the ENVIRONMENT_INITIALIZER Injection Token in Angular - Netanel Basal](https://netbasal.com/getting-to-know-the-environment-initializer-injection-token-in-angular-9622cb824f57)
9. [Typed Reactive Forms in Angular — No Longer a Type Dream - Netanel Basal](https://netbasal.com/typed-reactive-forms-in-angular-no-longer-a-type-dream-bf6982b0af28)
10. [Handling Page Titles in Angular - Netanel Basal](https://netbasal.com/handling-page-titles-in-angular-40b53823af4a)
11. [Angular Standalone Components: Welcome to a World Without NgModule - Netanel Basal](https://netbasal.com/angular-standalone-components-welcome-to-a-world-without-ngmodule-abd3963e89c5)
12. [Pass an Injector to Embedded Views - Netanel Basal](https://netbasal.com/new-in-angular-13-pass-an-injector-to-embedded-views-cb71155025b4)
13. [Setting page title from the route module - Brandon Roberts](https://dev.to/brandontroberts/setting-page-titles-natively-with-the-angular-router-393j)
14. [Standalone components with Custom Title Strategy in Angular 14](https://medium.com/geekculture/standalone-components-with-custom-title-strategy-in-angular-14-aec71a23bcd8)