Angular 18 standalone components
Standalone components in Angular offer a range of advantages that might not immediately be apparent. In this guide, we'll delve deep into the world of standalone components, exploring why they're superior to NgModule-based components and debunking common misconceptions along the way.
In the upcoming sections, we'll explore the numerous benefits of standalone components and shed light on why certain perceived drawbacks are not as significant as they may seem.
Furthermore, we'll discover how standalone components can significantly enhance your application's performance with minimal need for extensive refactoring.
Lastly, we'll discuss strategies for seamlessly migrating your application to utilize standalone components, allowing you to harness these benefits without disrupting your existing codebase.
What are Standalone Components?
In Angular, a standalone component is one that does not belong to any specific Angular module. Before Angular 14, components were typically declared within the declarations
array of a module. Attempting to use a component outside of this module would result in Angular throwing an error during compilation. However, starting from Angular 14, you have the option to create components that exist independently of any module, hence termed as standalone components.
In addition to standalone components, Angular 14 also introduced the ability to create:
- Standalone directives
- Standalone pipes
Standalone components can be utilized in various contexts, including:
- Within module-based components
- Alongside other standalone components
- Within route configurations for lazy loading
This flexibility allows developers to organize and structure their Angular applications more dynamically, enabling the seamless integration of standalone components alongside module-based components and lazy loading configurations.
How to Create Standalone Artifacts with Angular CLI
To create a standalone component, pipe, or directive using the Angular CLI, you can utilize the --standalone
flag with the ng generate
command. Here's how you can do it for each type:
- Standalone Pipe:
ng generate pipe mypipe --standalone
- Standalone Directive:
ng generate directive mydirective --standalone
- Standalone Component:
ng generate component mycomp --standalone
By adding the --standalone
flag, you instruct the Angular CLI to generate the specified element as standalone, allowing it to exist independently of any specific Angular module.
Understanding Standalone Components
To better understand the distinction, let's compare a typical component with a standalone one:
@Component({
selector: "my-comp",
template: `Hello Angular 18`,
})
class MyComponent {
}
The above code depicts a standard component that requires declaration within an NgModule to be effectively used. Without such declaration, it remains inaccessible for use within the template of other components.
In Angular, regular components must be declared within an NgModule to be used in the template of another component. This declaration is necessary for Angular's dependency injection system to recognize and manage the component.
For instance, in our main application module (AppModule
), we include the MyComponent
declaration within the declarations
array:
@NgModule({
declarations: [AppComponent, MyComponent],
imports: [BrowserModule, FormsModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {
}
This setup ensures that MyComponent
is available for use throughout our application.
Now, let's explore how we can transform MyComponent
into a standalone component.
To transform HelloComponent
into a standalone component, we simply need to add the property standalone: true
in its @Component
decorator:
@Component({
selector: "my-comp",
template: `Hello Angular 18`,
standalone: true,
})
class MyComponent {
}
That's all it takes!
Now, to use this standalone component, we import it into the component where we want to use it. For instance, let's assume we want to use MyComponent
in the AppComponent
:
@Component({
selector: "app",
template: `<my-comp></my-comp>`,
imports: [MyComponent],
})
class AppComponent {}
As illustrated, we only need to import it in the imports
array, and we're good to go!
It's important to note that without the import, the AppComponent
will not function as expected, and often, no error will be thrown.
Using Regular Modules/Directives/Pipes in Standalone Components
To use regular directives in standalone components, Angular offers most of its built-in directives as standalone directives. To employ them in standalone components, we simply need to include them in the imports
array of the component:
import { Component, Input } from '@angular/core';
import { Movie } from '../../models/movie';
import { DatePipe } from '@angular/common';
import { RouterModule } from '@angular/router';
@Component({
selector: 'app-movie',
standalone: true,
imports: [DatePipe, RouterModule],
templateUrl: './movie.component.html',
styleUrl: './movie.component.css'
})
export class MovieComponent {
@Input() movie!: Movie;
}
In the template of the standalone component, we can use these directives as normal:
<a routerLink="/show-movie/{{ movie.id }}">
<div>
<p>
{{ movie.release_date | date }}
</p>
</div>
</a>
In this example, we've imported the date pipe and router module into the imports
array of the MovieComponent
component, enabling us to use them in the template.
Omitting this imports would result in the component not functioning as expected.
Why Standalone Components?
Initially, standalone components may not seem like a significant departure from regular components, as they replace the concept of NgModules with manual imports. However, there are several compelling reasons why standalone components are considered superior:
Improved Developer Experience:
One of the primary motivations behind introducing standalone components in Angular was to streamline the developer experience by removing the need for NgModules. The concept of NgModules could be confusing for beginners and seemed unnecessary. With standalone components, developers can create components without having to declare them within any module, making the development process more straightforward and intuitive.
Manual Imports are Manageable:
At first, the requirement for manual imports in standalone components may have seemed burdensome. Even core directives like ngClass or ngStyle needed to be imported manually in each standalone component. This approach initially raised concerns about maintenance, as it seemed contrary to the benefits of NgModules, where dependencies could be imported once within a module. However, modern IDEs equipped with tools like the Angular Language Service can now automatically import dependencies for standalone components, mitigating the burden of manual imports and making the process seamless.
Facilitates Fully Lazy-Loaded Applications:
While removing NgModules is an advantage of standalone components, it's not their primary benefit. The real game-changer is how standalone components enable the development of fully lazy-loaded applications or the migration of existing monolithic applications to fully lazy-loaded ones. Many Angular applications developed with NgModules tend to be relatively monolithic, lacking proper modularization. Standalone components make it remarkably easy to transition such applications to fully lazy-loaded ones. By leveraging Angular CLI and making minor adjustments to routing configurations, developers can quickly transform every screen of an application into a separate lazy-loaded module. This results in significant performance improvements, as each screen is offloaded to a separate bundle, reducing the main application bundle's size.
In essence, standalone components not only simplify Angular development and make it more beginner-friendly but also offer a straightforward path to developing fully lazy-loaded applications or migrating existing ones to achieve optimal performance. In the following sections, we'll explore how to leverage standalone components for lazy loading. But before diving into that, let's examine other constructs besides components that can be made standalone.
Standalone pipes
To create a standalone pipe, we follow a similar approach to standalone components. Here's an example of how to create a standalone pipe:
import { Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'customCurrency',
standalone: true
})
export class CustomCurrencyPipe implements PipeTransform {
transform(value: number, currencyCode: string): string {
switch (currencyCode) {
case 'USD':
return '$' + value.toFixed(2);
case 'EUR':
return '€' + value.toFixed(2);
default:
return value.toFixed(2);
}
}
}
And just like pipes, we can also create standalone directives.
Using NgModule-based Components in Standalone Components
Standalone components can indeed be used within NgModule-based components, offering versatility and modularity in Angular applications. Let's delve into how you can achieve this integration:
- Importing Standalone Components: Begin by importing the desired standalone component into your NgModule-based component:
import { StandaloneComponent } from './path/to/standalone-component';
- Declaring Standalone Components:
Next, declare the standalone component within the
@NgModule
decorator'sdeclarations
array:
@NgModule({
declarations: [
AppComponent,
StandaloneComponent
],
imports: [BrowserModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule { }
- Utilizing Standalone Components: You can now utilize the standalone component in your NgModule-based component's template:
<app-standalone></app-standalone>
By integrating standalone components into NgModule-based components, you enhance the flexibility and reusability of your application's architecture. This approach enables you to modularize your application effectively, allowing for seamless integration of standalone functionalities within larger NgModule-based structures.
Using NgModule-based Components in Standalone Components
Integrating NgModule-based components into standalone components is a straightforward process in Angular. Let's explore how to accomplish this:
First, create an NgModule that declares and exports the desired NgModule-based component:
@NgModule({
declarations: [MyComponent],
// Export the NgModule-based component
exports: [MyComponent],
})
export class MyModule {
}
Next, import the NgModule into the standalone component:
@Component({
selector: "app-standalone",
template: `Standalone component`,
standalone: true,
// Import the NgModule that declares the component
imports: [MyModule],
})
class StandaloneComponent {}
With this setup, NgModule-based components can be seamlessly used within the template of the standalone component, just like any other component. Importing the entire NgModule ensures that any other components exported by the NgModule can also be utilized within the standalone component's template.
This interoperability between NgModule-based components and standalone components facilitates flexible component composition within Angular applications, allowing developers to mix and match both types of components without encountering any compatibility issues.
Dependencies in Standalone Component
To add dependencies to a standalone component, you can use the imports
array within the @Component
decorator. These dependencies can be of two types: standalone or part of a module.
Here's an example of adding dependencies to the AComponent
:
import { Component, OnInit } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ReactiveFormsModule } from '@angular/forms';
@Component({
selector: 'app-a',
standalone: true,
imports: [CommonModule, ReactiveFormsModule],
templateUrl: './a.component.html',
styleUrls: ['./a.component.css']
})
export class AComponent implements OnInit {
}
We import CommonModule
and ReactiveFormsModule
, which are standalone and module-based dependencies, respectively.
Both dependencies are added to the imports
array of the @Component
decorator.
This allows LoginComponent
to use directives, pipes, and other features provided by CommonModule
and ReactiveFormsModule
.
Similarly, to use a module-based component inside a standalone component, you can import that module and include it in the imports
array of the standalone component.
-
Date: