What is new in Angular v14 - A Game Changer

Discover what is new in Angular 14

LinkIntroduction

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!

LinkStandalone components

For most people, the most significant change in this version is the possibility of creating components without @NgModules! Yeah, you got that right.

Image description

Before Angular 14

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

bash
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.

bash
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.

TypeScript
@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 @NgModules will not be deprecated.

As a matter of fact. I will still use @NgModules instead of standalone components because I like the isolation that @NgModules 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.

LinkRoute 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 @NgModules?

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.

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

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

LinkENVIRONMENT_INITIALIZER

Another thing we used to do with NgModules 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.

TypeScript
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.

TypeScript
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 constructors and has the added benefit of being more explicit.

LinkTyped 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.

TypeScript
  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.

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

LinkThe 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.

TypeScript
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:

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.

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

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

LinkSet 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.

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

But I know what you're thinking:

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.

TypeScript
@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 }
  ]
});

LinkAngular 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.

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

> ng serve
TypeScript
> 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.

LinkOptional 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.

TypeScript
@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'
            }
          ]
        })
      }
    );
  }
}

LinkDon'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. 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 that here.

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

– Lucas

LinkReferences

  1. Angular 14 - Academind on Youtube
  2. O que há de novo no revolucionário Angular 14 - Andrew Rosário on Medium
  3. Novidades no Angular v14 - Vida Fullstack
  4. What is new in Angular 14? - Nevzatopcu on Medium
  5. Angular v14 is now available! - Angular Blog on Medium
  6. What’s New in Angular v14 - Netanel Basal
  7. Unleash the Power of DI Functions in Angular - Netanel Basal
  8. Getting to know the ENVIRONMENT_INITIALIZER Injection Token in Angular - Netanel Basal
  9. Typed Reactive Forms in Angular — No Longer a Type Dream - Netanel Basal
  10. Handling Page Titles in Angular - Netanel Basal
  11. Angular Standalone Components: Welcome to a World Without NgModule - Netanel Basal
  12. Pass an Injector to Embedded Views - Netanel Basal
  13. Setting page title from the route module - Brandon Roberts
  14. Standalone components with Custom Title Strategy in Angular 14

Join our Newsletter and be the first to know when I launch a course, post a video or write an article.

This field is required
This field is required