<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 https://www.facebook.com/tr?id=1063935717132479&amp;ev=PageView&amp;noscript=1 "> Bitovi Blog - UX and UI design, JavaScript and Front-end development
Loading

Angular |

Angular 14: Three Can't-Miss New Features

Angular 14 has new features you won't want to miss! In this post, we're breaking down our top three favorite new features from Angular 14.

Igor Ciric

Igor Ciric

Twitter Reddit

Angular 14 introduced three big, new features:

  • Standalone components
  • Typed Forms
  • New “inject” function

In this blog, we’ll explore the new features in Angular 14 using a Movie Search Application example. You can check out the project source code, which includes everything we will cover. You can see how the project looks here.

Standalone Components

The new approach of using the standalone components can help us reduce unnecessary code, especially for small projects. Standalone components don't need to be declared in a module.

We can create our first standalone component using CLI command:

ng g c name-of-the-component --standalone

This is the @Component decorator generated by running the ng generate component command with the standalone flag.

@Component({
  selector: 'app-name-of-the-component',
  standalone: true,
  imports: [CommonModule],
  templateUrl: './name-of-the-component.component.html',
  styleUrls: ['./name-of-the-component.component.scss']
})

The Component decorator now has a new property called standalone that has the value set to true. To show how to use standalone components in practice, we will build a MovieSearch Component using ReactiveFormsModule and a child component, MovieCard.

@Component({
  selector: 'app-movie-search',
  standalone: true,
  imports: [CommonModule, ReactiveFormsModule, MovieCardComponent],
  templateUrl: './movie-search.component.html',
  styleUrls: ['./movie-search.component.scss']
})

We can see standalone components explicitly manage their own dependencies with the imports array.

Standalone components imports array only accepts other standalone components or Angular modules, but not modules with providers.

The router navigation for standalone components works the same as before for modules. Below you can see how we use lazy loading for our MovieInfo component using loadComponent.

export const routes: Routes = [
  { path: '', pathMatch: 'full', redirectTo: 'search' },
  { path: 'search', title:'Movie Search', component: MovieSearchComponent },
  { path: 'movie/:imdbID', title:'Movie Information', loadComponent: () => import('./movie-info/movie-info.component').then(m => m.MovieInfoComponent) }
];

The version of Angular 14 comes with a new property of the router, title, that allows us to improve accessibility by changing the title of our pages.

We can make modules optional and run our application from a standalone component. In the main.ts file, we will use the bootstrapApplication function, and we will provide our standalone component as a first parameter.

bootstrapApplication(AppComponent, {
  providers: [
importProvidersFrom(RouterModule.forRoot(routes)),
importProvidersFrom(HttpClientModule),
MoviesService
  ]
})
  .catch(err => console.error(err));

You can start using standalone components today, they are compatible with your existing applications! 

In Angular version 14 standalone components are enabled as a developer preview, meaning that the API for these new types of components is not stable. From version 15, standalone components are stable.

Typed Forms

The next big feature of Angular 14 is the implementation of Typed Forms.

<form [formGroup]="form" (ngSubmit)="onSubmit()" class="search-bar">
    <div class="form-input title">
        <label for="title">Title</label>
        <input type="text" id="title" formControlName="title" placeholder="Search By Movie Title...">
    </div>
    <div class="form-input type">
        <label for="type">Type</label>
        <select id="type" formControlName="type">
            <option value="">All</option>
            <option value="movie">Movie</option>
            <option value="series">Series</option>
            <option value="episode">Episode</option>
        </select>
    </div>

    <button type="submit">Search</button>
</form>

As we can see in the template, the HTML part of the code looks the same as in older Angular versions for untyped forms. 

form = new FormGroup({
title: new FormControl<string>('', Validators.required),
type: new FormControl<string>(''),
  })

One of the benefits of using typed forms is autocompletion, which gives us insight into the only available values and their types. In our example, we have title and type. 

ng14

 

Without Typed Forms, we couldn't get that information since FormGroup was as type any before.

It is possible to use old FormControls using UntypedFormControl. This can be useful when upgrading from older versions of Angular. Running the command ng update will also automatically do those transformations for us so that all old FormControls will be transformed into UntypedFormControl.

form = new UntypedFormGroup({
title: new UntypedFormControl('', Validators.required),
type: new UntypedFormControl(''),
})

New “Inject” Function

The Inject function in Angular 14 helps us to simplify inheritance within our application. Our MovieBase abstract class injects the Router, ActivatedRoute, and MovieService, so our MovieCard component doesn't need to provide ActivatedRoute, MovieService, and Router inside the constructor and super() method like we did before. 

export abstract class MovieBase {
    protected route = inject(ActivatedRoute)
    protected moviesService = inject(MoviesService)
    protected router = inject(Router)
}
export class MovieInfoComponent extends MovieBase implements OnInit {

  movie$: Observable<Movie | undefined>

  constructor() {
    super()
  }

  ngOnInit(): void {
    this.movie$ = this.route.paramMap.pipe(
      switchMap(param => {
        const imdbID = param.get('imdbID')
        if (imdbID) {
          return this.moviesService.findById(imdbID)
        } else {
          return of()
        }
      }),
      tap((result)=>{
        if(result.Response==='False'){
          this.router.navigate(['/search'])
        }
      }))
  }
}

The inject function can also be used inside components, directives, and pipes.

Summary

The new Angular 14 comes with cool new features you can use in your new application or use to improve your existing one.

Learning to Love Angular 14?

We’re here for you! Our Angular consulting experts are standing by to help you with new features or update your existing application. Schedule your free consultation call to get started!