The Anatomy of an Angular App and its Component Tree
Jul 15, 2020 • 6 Minute Read
Introduction
What is an Angular app and what are its building blocks? What is the Angular component tree? This guide will answer these questions.
Angular apps generally have the following core pieces:
- Modules
- Services
- Components
In this guide, you will learn how you can use these building blocks to architect a modern app. Let's dive in!
Modules
Every Angular app/library consists of one or more modules. Modules are essentially the Angular framework's mechanism for encapsulating a portion of your app or library. Modules themselves consist of one or many services and components that all work together in tandem to comprise a given purpose or set of functionality. To create a module, you must either use the Angular CLI's ng generate module <module_name> command or manually create a class that is decorated with the @NgModule decorator. See the following for an example:
import { NgModule } from '@angular/core';
@NgModule({
imports: [ ],
providers: [ ],
declarations: [ ],
exports: [ ],
bootstrap: [ ]
})
export class TestModule { }
Aside from creating your own modules, Angular also provides a number of modules for you to use out of the box. Some of these modules include:
- FormsModule
- ReactiveFormsModule
- CommonsModule
- HttpClientModule
- RouterModule
The modules above come with built-in APIs for working with forms, templates, routing, and HTTP requests.
Module Best Practices
When using modules, there are a few best practices to be aware of. One best practice is to organize your modules by feature or app workflow. These types of modules are called "feature" modules. By creating feature modules, you can easily inject a given feature or workflow into just the portions of your app that you want to.
Another best practice is to create a single, shared module that contains components and services that are widely used across your app. By encapsulating all of the shared logic into a single module, you can easily import this shared module into the different feature modules that need it. Be careful! You don't want a shared module to become too large in size, as this can make it more difficult to create smaller chunks of javascript when lazy loading.
Finally, another best practice pertaining to modules in Angular is to use lazy loading in your app. Angular provides you with the ability to import modules dynamically, just when they are needed in your app. This means that rather than loading a single, larger JavaScript file when your app loads, you can create a chunked build where each appropriately marked module is split into its own separate JavaScript file after compilation. These individual JavaScript files are only loaded into the browser when they need to be used by the app. By effectively splitting your app's view layer into appropriate feature's modules, you can then lazy load each of these modules, splitting your production build into easily loaded chunks of JavaScript. This can improve the loading speed of your app dramatically!
Services
The Angular framework offers an additional abstraction in the form of what it calls a service. An Angular service works in tandem with the Angular framework's dependency injection system to give you the capability to inject a class into any portion of your app.
You can create a new Angular service by using the Angular CLI command ng generate s <service_name> or by manually creating a class and marking it with the @Injectable decorator.
You can mark an Angular service as a singleton (there will only be one instance of this service in your app) by using the providedIn field of the @Injectable decorator like this:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TestService {}
If instead you want multiple instances of your service to be available, you must first create your decorated service like this:
import { Injectable } from '@angular/core';
@Injectable()
export class TestService {}
Now you can inject this TestService within any of your Angular components by adding it into the providers array within the @Component decorator that Angular provides. Note this in the following:
@Component({
selector: 'app-test',
template: `<div></div>`,
providers: [ TestService ]
})
class TestComponent {
constructor(private testService: TestService) { }
}
The Component Tree
Angular also provides a third abstraction that covers the view layer of your app or library: components. Angular components are functionally comprised of a template (.html), business logic layer (.ts), and template styling (.css/.scss/.less).
The view layer of every Angular app is comprised of a component tree. Every Angular app is comprised of a root component. You then create various child components which are themselves comprised of child components. This constitutes the component tree and is an essential building block of your app. You can create an Angular component by using the Angular CLI command ng generate c <component_name> or by manually creating a class and attaching the @Component decorator to it. Please note the following example:
@Component({
selector: 'app-test',
templateUrl: './test.component.html'
})
export class TestComponent {
}
Conclusion
In this guide, you learned all about the anatomy of an Angular app. You learned how to use modules to break your Angular app into reusable, injectable chunks of code, which can also be lazy-loaded. You also learned how to use services to encapsulate core pieces of business logic and inject them at will into portions of your app architecture. Finally, you learned about the Angular component tree and how to structure the component tree of your own app in a way that makes the most sense for the problem you are trying to solve.
You can now be confident in building a modern Angular app that is architecturally sound and that follows best practices. For more information, please check out the Angular docs.