Standalone components and Nx . Component-first architecture with

When Angular 2 released its first pre-release version, it had no concept NgModule, we attached Components as Directives for the components they needed.

import { Component } from @angular/core’;
import { ButtonComponent } from ./button.component;

@Component({
   ,
   directives: [ButtonComponent]
})
export class HomeComponent {}
enter fullscreen mode

exit fullscreen mode

But all this changed in Angular 2-rc.5. This version saw the introduction of NgModules, Additional metadata was needed for the compiler and for dependency injection systems to help build the injection hierarchy accurately.
It completely changed how Angular apps were architected and designed. It also added a level of complexity to building Angular apps and another concept new Angular developers had to learn and understand.

Alternative NgModules It soon became the top feature request on the Angular repository.

Introduction to Standalone Components

Over the past year, the Angular team tackled this and the solution is standalone components. issued under Developer Preview During the last few iterations of the label Angular 14, they will be officially marked as stable in Angular 15.

Standalone components provide a much simpler approach to application development and better fit the current trend of frontend development focused on structuring components.

Let’s take a look at some of the differences now that we have standalone components.

make components

Creating components in Angular basically requires that we create a component and declare it in an existing or new NgModule. With the standalone components, we now simply set up standalone: true property in component decorator metadata and now we don’t need to declare it in NgModule.

If the standalone component requires components, pipes, or directives that are exported from an existing NgModule or standalone component, pipe or directive, we simply need to use new imports: [] property on the component decorator metadata to import them. You can see an example of the changes below:

using standalone components

As mentioned above, standalone components can be imported to allow other standalone components to be used directly in your template. Standalone components can also be imported into NgModules if you need to maintain some level of interoperability between NgModules and standalone components.

standalone component

Way

With NgModules optional, a new method is needed to route through our applications without using RouterModule.forRoot() And RouterModule.forChild(),

The Angular team has created some standalone APIs that provide this functionality without using NgModule equivalent. provideRouter replaces the API RouterModule.forRoot(), They also enable lazy-loading of route constants, which means we can directly reference other exported route declarations instead of just NgModules.

Way

test

It becomes much easier to test with standalone components. We no longer need to configure TestBed to include all the NgModules that our component needs to function because the component itself already imports them. The only additional configuration you may find yourself doing with TestBed would be Mocks and Spies.

test

Bootstrap an Application

Previously, bootstrapping an application involved two separate attempts:
Telling Angular Which NgModule to Bootstrap
Describing which component the NgModule should bootstrap

NgModule used to bootstrap (usually AppModule) will also include all implemented root-level providers and NgModules .forRoot,

With standalone components, there is a new function, bootstrapApplication, which replicates this functionality, but allows you to tell Bootstrap to be a standalone component with all of the root-level providers on it. For example, this is where we would add provideRouter,

bootstrapping

It is already becoming clear that building our Angular applications with only Standalone Components, Pipes, Directives and APIs is much simpler and more straightforward than working with NgModules. However, NgModules provided a clear concept for architecting our applications with patterns such as CoreModules, SharedModules and FeatureModules.

This leaves a gap for the use of similar patterns when using standalone components.

What is Component-First Architecture?

Component-first architecture is a pattern for the architecture of our applications to fill the void left by the patterns that emerged around NgModules. It also has a basic concept.

The concept that your application is completely controlled by your components

Component-I architecture is composed of four main pillars.

  1. Standalone Components and Declarables
  2. low provider signal
  3. Dedicated route file/component
  4. Component-led state management

component-first architecture

Standalone Components and Declarables

Application must be a combination of standalone components, pipes and instructions

Standalone Declarables import exactly what they need to operate. This makes the logic of their working even more straightforward. Testing becomes easier as there is less setup to do on the testing side. Debugging becomes very easy and we can see everything affecting the component, pipe or directive just by looking at its decorator metadata.

I also believe that dev tools in general can be smarter. All of our files will have direct ts imports to the other files they depend on. With this information, we can build an architecture graph that can help find circular dependencies as well as enable NX affect-like incremental building of our applications.

Pillar 1 - Standalone Declarables

low provider signal

Services that can be used globally must use `provided: ‘root’, while component-specific services must be provided directly in that component’s decorator metadata.

This again feeds into the idea that we can quickly see exactly what is needed by looking at the decorator metadata, making it easier to reason thoroughly about our application and address issues related to dependency injection hierarchies. Can help prevent which may cause too many instances of services which we expect to be singleton.

Column 2 - Low Provider Injection

dedicated route file

There should be a dedicated *.routes.ts file for each feature/domain as the entry point

By having a dedicated routes file as the entry point to our features, we can reimplement the FeatureModule pattern we have become accustomed to with NgModules, while having the added benefit of being able to easily identify where our routing is. Where is the configuration in our entire application. We can also use it to map routing throughout our application.

Column 3 - Dedicated Route File

Component-led state management

Our components should lead in how we manage state within our application. Use a global store for shared state, otherwise offload component and feature related state to our components.

Using tools like the NgRx Component Store, each component can manage its state using reactive methods. It gives less indication of how the state is managed for the component. It is provided directly to the component and therefore continues to fill in the philosophy that we should know everything about our component just by looking at its decorator metadata. All children of the component can access it and therefore allows a child -> parent -> child data flow.

Even with the NGRX component store, global state management can still be achieved, allowing us to set up dedicated stores for shared state within our application. All we have to do is add providedIn: ‘root’ for the service to the store and then we can inject it into any component or service/component store that requires it.

Finally, feature-level stores can also be built. Since component stores are provided as a service, if we create a component store for a complete feature and inject it into the ingress component of a feature, all children of that feature can access the store and its can talk with.

Pillar 4 - Component Led State Management

Example directory structure

Following this pattern we will end up with a folder structure where it is easy to identify features, routing configuration and where state is managed throughout our application.

Example directory structure

How can NX help?

NX provides three main elements that can greatly assist in following this architectural pattern.

  • Generators – to help us easily scaffold the code, following the guidelines.

  • Standalone component support – at a much higher level than is currently offered by the Angular CLI.

  • Workspace Libraries – To help us break down features by domain into separate libraries that only offer public APIs exporting our entry points.

The generators offered by nx not only allow us to generate applications and libraries that are setup to use standalone components, they also allow us to automatically attach routing configurations for new libraries to existing routes files. allow.

Below is a list of commands that will scaffold our architecture and automatically wire up our routing configuration.

nx command

You can watch a video of some of these here: https://www.youtube.com/watch?v=e-BpE9d3NIw

conclusion

Standalone component. provide much better DX than NgModule applications but initially there was a difference in how we should build our applications with just standalone components. This article talks through an approach that should make it easy and straightforward to continue to build Angular applications in a way that we are familiar with and that is based on DX as much of the standalone components.


learn more

nx docs
nx github
NRL Community Slack
nrl youtube channel
Free Egghead Course
Need help with Angular, React, Monorepos, Lerna or Nx? talk to us

Also, if you liked it, be sure to click and follow column And NX On Twitter for more!

Leave a Comment