Angular 2 Architecture
An Angular 2 application consists primarily of the following 8 parts:
- Modules
- Components
- Templates
- Metadata
- Data Binding
- Directives
- Services
- Dependency Injection
The diagram below shows how each part interacts with the others:
The template is composed of Angular-extended HTML syntax, the component class manages these templates, the application logic is completed through services, then services and components are packaged in modules, and finally, the application is started by bootstrapping the root module.
Next, we will analyze the above 8 parts separately:
Modules
A module is composed of a block of code that can be used to perform a simple task.
Angular applications are modular and have their own modular system: NgModules.
Each Angular application should have at least one module (the root module), typically named: AppModule.
An Angular module is a class decorated with the @NgModule decorator, which receives a metadata object that describes the module's properties.
Several important properties are:
- declarations - View classes that belong to this module. Angular has three types of view classes: components, directives, and pipes.
- exports - A subset of declarations that can be used in component templates of other modules.
- imports - Modules whose exported classes are needed by component templates in this module.
- providers - Creators of services. This module adds them to the global service collection, making them accessible anywhere in the application.
- bootstrap - The main view of the application, called the root component, which is the host of all other application views. Only the root module needs to set the bootstrap property.
A simple root module example:
app/app.module.ts File:
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
imports: [ BrowserModule ],
providers: [ Logger ],
declarations: [ AppComponent ],
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
Next, we start the application by bootstrapping the root module. The development process usually bootstraps the AppModule in the main.ts file, as follows:
app/main.ts File:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
platformBrowserDynamic().bootstrapModule(AppModule);
Components
A component is a control class for handling the application and logical view of the page.
Components are the foundation and core of an Angular application and can be used throughout the entire application.
Components know how to render themselves and configure dependency injection.
Components interact with the view through an API consisting of properties and methods.
There are three steps to create an Angular component:
- Import the Component decorator from @angular/core
- Create a regular class and decorate it with @Component
- In @Component, set the selector (custom tag) and template (template)
Templates
The default language for Angular templates is HTML.
We can use templates to define the view of a component to tell Angular how to display it. Here is a simple example:
<div>
Website Address: {{site}}
</div>
In Angular, double curly braces are used as the interpolation syntax, with the value inside usually being a variable name of a component property.
Metadata
Metadata tells Angular how to process a class.
Consider the case where we have a component called Component, which is a class until we tell Angular that it is a component.
You can attach metadata to this class to tell Angular that Component is a component. In TypeScript, we use decorators to attach metadata.
Example
@Component({
selector: 'mylist',
template: '<h2>tutorialpro.org</h2>',
directives: [ComponentDetails]
})
export class ListComponent {...}
The @Component decorator can accept a configuration object and mark the class that follows it as a component class.
Angular creates and displays the component and its view based on this information.
Configuration options in @Component:
selector - A CSS selector that tells Angular to look for a
<mylist>
tag in the parent HTML, create the component, and insert it into this tag.templateUrl - The URL of the component's HTML template.
directives - An array containing the components or directives that this template depends on.
providers - An array containing the dependency injection providers required by the services that the component depends on.
Data Binding
Data binding provides a simple and consistent method for applications to display data and interact with data. It is a mechanism for managing values within an application.
With this mechanism, values can be retrieved and assigned from HTML, making data reading, writing, and persistence operations simpler and faster.
As shown in the figure, there are four forms of data binding syntax. Each form has a direction—from the DOM, to the DOM, or bidirectional, as indicated by the arrows in the figure.
Interpolation: Displays the component value within an HTML tag.
<h3> {{title}} <img decoding="async" src="{{ImageUrl}}"> </h3>
Property Binding: Sets the element's attribute to the value of a property in the component.
<img [src]="userImageUrl">
Event Binding: Triggers when a component method is clicked.
<button (click)="onSave()">Save</button>
Two-way Binding: Using Angular's NgModel directive makes two-way binding more convenient.
<input [value]="currentUser.firstName" (input)="currentUser.firstName=$event.target.value" >
Directives
Angular templates are dynamic. When Angular renders them, it modifies the DOM according to the directives.
A directive is a class with "directive metadata." In TypeScript, you attach metadata to the class using the @Directive decorator.
There are three types of directives in Angular:
Attribute Directives: Directives used as element attributes.
Structural Directives: Used to change the structure of the DOM tree.
Components: A significant subclass of directives, essentially a directive with a template.
<li *ngFor="let site of sites"></li> <site-detail *ngIf="selectedSite"></site-detail>
*ngFor tells Angular to generate an <li>
tag for each item in the sites list.
*ngIf includes the SiteDetail component only if the selected item exists.
Services
Services in Angular 2 are encapsulated modules that provide specific functionality and can be used by others through injection.
Services come in many types, including values, functions, and features required by the application.
For example, when duplicate code appears in multiple components, extracting it into a service allows for code reuse.
Common services include:
Logging Service
Data Service
Message Bus
Tax Calculator
Application Configuration
The following example is a logging service that logs to the browser console:
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}
Dependency Injection
Inversion of Control (IoC) is a design principle in object-oriented programming that can be used to reduce the coupling between computer code. The most common method is Dependency Injection (DI), and another method is "Dependency Lookup."
Through IoC, when an object is created, an external entity that controls all objects in the system passes the references of the objects it depends on to it. In other words, dependencies are injected into the object. In the traditional development model, the caller is responsible for managing all object dependencies, and circular dependencies have always been a nightmare. In the dependency injection model, this management is delegated to the injector (Injector), which is responsible for replacing dependency objects at runtime rather than at compile time. This inversion of control and the ability to inject dependencies at runtime are the essence of dependency injection.
Angular can determine which services a component needs by examining the parameter types of the constructor. For example, the SiteListComponent constructor requires a SiteService:
constructor(private service: SiteService) { }
When Angular creates a component, it first looks for an injector (Injector) for the services required by the component.
The injector is a container that maintains service instances, storing previously created instances.
If the requested service instance is not in the container, the injector creates a service instance, adds it to the container, and then returns this service to Angular.
After all services are resolved and returned, Angular calls the component's constructor with these services as parameters. This is dependency injection.