Angular Standalone Components

Angular Standalone Components

Getting started with standalone components

This article discusses the latest feature in Angular called standalone components. Traditionally, developers had to create or update an existing NgModule to use a newly created component. NgModules were used to specify the available components, directives, and pipes for templates. However, with the new standalone API, it is now possible to write Angular components, directives, and pipes without creating an associated NgModule.

What are Standalone Components?

Standalone components are self-contained and manage their template dependencies, making NgModule optional for many development tasks. This shift towards standalone components simplifies the learning journey and reduces the amount of code and files required. The benefits of this new approach are significant. Creating a new component becomes easier with less code to write and fewer files to modify. Moreover, it reduces the number of concepts developers need to understand, making Angular more approachable.

Most importantly, standalone components can be packaged, reused, and lazily loaded independently, enabling new use cases that were previously challenging or even impossible.

To demonstrate the new APIs, let's go over the new features step-by-step -

The standalone flag and component imports

Components, directives, and pipes can now be marked as standalone: true. Angular classes marked as standalone do not need to be declared in an NgModule. Standalone components specify their dependencies directly inside it instead of specifying them through NgModules. It can directly import other standalone components, directives, and pipes.

@Component({
  standalone: true, // this makes it a standalone component
  selector: 'standalone-component',
  imports: [OtherStandaloneComponent],
    // other standalone components or dependencies can be imported
  template: `
    ... <other-standalone-component></other-standalone-component>
  `,
})
export class StandaloneComponent {
  // component logic
}

imports can also be used to reference standalone directives and pipes. In this way, standalone components can be written without the need to create an NgModule to manage template dependencies.

Using existing NgModules in a Standalone Component

When writing a standalone component, you may want to use other components, directives, or pipes in the component's template which are not marked as standalone but instead declared and exported by an existing NgModule. In this case, you can import the NgModule directly into the standalone component:

@Component({
  standalone: true,
  selector: 'my-app',
  // an existing module is imported directly into a standalone component
  imports: [MatButtonModule],
  template: `
    ...
    <button mat-button>Next Page</button>
  `,
})
export class AppComponent {
  // component logic
}

Using standalone components in NgModule-based applications

Standalone components can also be imported into existing NgModules-based contexts. This allows existing applications to incrementally adopt the new standalone style of component.

@NgModule({
  declarations: [AppModule],
  exports: [AppComponent], 
  imports: [StandaloneComponent],
//standalone component can be imported the same way as normal components
})
export class AppModule {}

Bootstrapping app with standalone component

An Angular application can be bootstrapped without any NgModule by using a standalone component. This component can be used as the application root component. This can be achieved using bootstrapApplication API:

// in the main.ts file
import {bootstrapApplication} from '@angular/platform-browser';
import {MainComponent} from './app/main.component';

bootstrapApplication(MainComponent);

Configuring dependency injection

When bootstrapping an application, we can configure Angular’s dependency injection and provide configuration values or services to be used throughout the application. Add these services and values as providers to bootstrapApplication.

bootstrapApplication(MainComponent, {
  providers: [
    {provide: BACKEND_URL, useValue: 'https://...'},
    {provide: ErrorHandler, useClass: GlobalErrorHandler}
    // ...
  ]
});

If a library only offers an NgModule API for its DI configuration, you can use the importProvidersFrom utility to still use it with bootstrapApplication and other standalone contexts

import {LibraryModule} from 'library';

bootstrapApplication(AppComponent, {
  providers: [
    {provide: ErrorHandler, useClass: GlobalErrorHandler},
    //    ...
    importProvidersFrom(
      LibraryModule.forRoot() // from module based library
    ),
  ]
});

Summary

We have now successfully created standalone components, managed their template dependencies. We have also seen how other module-based library or dependency can be included in standalone components and how it is used with the existing module-based applications. In the next article we learn about routing to navigate between pages rendered by standalone components. These new functionalities and APIs are available from Angular version 14 onwards and are fully compatible with existing Angular applications, requiring no rewrites or breaking changes.

Did you find this article valuable?

Support Code Craft - Fun With Javascript by becoming a sponsor. Any amount is appreciated!