An overview of Angular

Mátyás Lancelot Bors
17 min readMar 28, 2018

--

Image from Pexels

In this article, we are going to have a look at the Angular framework. Let’s get into it!

Introduction

Nowadays, we have plenty of options to develop something that is ready for various platforms. However, Angular has made its way and it is now one of the most important actors. Let’s see what it is and how it works.

We could jump right into the code of a project, but we would probably miss a few things. So, here, we are going to look at the architecture of Angular to understand the different concepts and elements this last one uses.

What is Angular?

Now, when we talk about Angular, we talk about Angular 2 or Angular 5. Angular is a complete rewrite of the AngularJS framework. Angular as a different approach from its predecessor.

Angular allows us to build applications across all platforms. It is an open-source platform that uses TypeScript. In a few words, TypeScript is a strict syntactical superset of JavaScript, and adds optional static typing to the language.

Architecture overview

Angular is written in TypeScript and it implements core and optional functionality as a set of TypeScript libraries that we can import.

An Angular application has, for building blocks, a thing called NgModules. An Angular app is defined by a set of NgModules. An app always has at least a Root Module that enables bootstrapping. An NgModule is made of Components. Every app has at least a Root Component.

Components, and things like Services, are just classes. They are, however, marked with decorators that tells Angular how to use them.

Angular provides a Router Service that helps us to define navigation paths among the different Views.

Modules

Angular apps are modular and this modularity system is called NgModules.

An NgModule defines a set of Components. An NgModule associate related code to form functional units. Every Angular app has a Root Module, conventionally named AppModule, which provides the bootstrap mechanism that launches the application.

Even if they are different and unrelated, NgModules, like JavaScript modules, can import functionality from other NgModules, and allow their own functionality to be exported and used by other NgModules. What we call Angular Libraries are NgModules.

We declare an NgModule by decorating our class with the “@NgModule” decorator. This decorator is a metadata object whose properties describe the module. The most important properties, which are arrays, are the following:

  • declarationsComponents, Directives, and Pipes that belong to the NgModule
  • exports — the subset of declarations that should be visible and usable in the Components of other NgModules
  • imports — other modules whose exported classes are needed by Components declared in the NgModule
  • providers — list of the needed Services that, because they are listed here, become are available app-wide
  • bootstrap — the main application View, called the Root Component, which hosts all other app views. (only the Root Module should set this bootstrap property)

An NgModule provides a compilation context for its various Components. So, the Components that belong to an NgModule share a compilation context. NgModules define a cohesive block of functionality.

The Root Module of our application is the one that we bootstrap to launch the application. The application launches by bootstrapping the root AppModule. We also call it the entryComponent. The bootstrapping process creates the Components listed in the “bootstrap” array and inserts each one into the browser DOM. So, each bootstrapped Component is the base of its own tree of Components.

As we saw, we can have a Root Module, but we can have what we call Feature Modules. A Feature Module delivers a cohesive set of functionality focused on a specific application needs. We could do everything in the Root Module, but a Feature Module will help us partition our app into focused areas. However, the structure of a Feature Module is exactly the same as the one of a Root Module.

Down below, we can find an example of how could look an NgModule. Here, it is the AppModule:

// Importing Angular Libraries
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
// Importing the AppComponent
import { AppComponent } from './app.component';
// Importing a custom feature module
import { CustomFeatureModule } from './custom-feature-module/custom-feature-module.module';
// Declaring the Module
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule,
HttpModule,
CustomerDashboardModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }

Components

A Component controls a patch of screen called a View. The logic of a Component is defined inside a class. Angular creates, updates, and destroys Components as the user moves through the application.

A Component is identified by the “@Component” decorator that has a set of properties. The most import properties are the following ones:

  • selector — tells how the component is referenced in HTML; in simple words, it corresponds to the HTML tag.
  • templateUrl — gives the path of the HTML template.
  • providers — an array of Dependency Injection Providers for Services that the Component requires.

Notice that instead of the “templateUrl” property, we could use the “template” property that lets us provide the HTML template inline.

A Component has a View and this last one is defined through an HTML template. This HTML file also contains some syntactic elements that are included in Angular.

A Component will typically look like so:

@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
providers: [ MyService ]
})
export class MyComponent implements OnInit {
// Some code
}

Before going any further with Components, let’s take a look at a few other elements to simplify some terms that we will use later.

Services

A Service is useful to define things that can’t fit into a Component and find their reason to exist in the separation of concerns. A Service is a class with a well-defined purpose. For example, we should create a Service when two or more Components or other things need to access the same data or if we want to encapsulate interactions with a web server or if we want to define how to validate user inputs.

Services are Singletons, so there is only one instance of each Service we define. They are stateless objects that can be invoked from any Components. Their purpose is to help us to divide our application into small, different logical units that can be reused.

A Service is a simple class and could look like so:

export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}

Dependency Injection

Dependency Injection is a large subject. Dependency Injection, also called DI, is a Design Pattern in which one or more dependencies (Services) are injected into a dependent object (Client). This pattern allows us to implement a loosely coupled architecture by separating the creation of a client’s dependencies from its own behavior.

We can apply this pattern when we want to remove knowledge of concrete implementations from objects, but also when we want to get a better testable code in isolation using mock objects.

The DI Pattern is commonly used to implement the Inversion of Control Principle, which in a few words, separates the what-to-do part of the when-to-do part. In other words, it is about letting somebody else handles the flow of control. It is based on the Hollywood Principle: “Don’t call us, we’ll call you”.

Dependency Injection could be achieved by using the “constructor” of a class or “setter” methods. It can also be achieved with a Container that handles the instantiation of other objects.

In Angular, DI is widely used and we can take a moment to dig a little into it.

Angular uses its own Dependency Injection framework that basically uses three things:

  • The Injector, that exposes APIs. It is responsible for creating Service instances and injecting them into classes.
  • The Provider, that tells the Injector how to create an instance of a dependency.
  • The Dependency, the type of which an object should be created.

Angular has a Hierarchical Dependency Injection system. There is a tree of Injectors that parallels an application’s Component tree. An application may have multiple Injectors. That means we can configure Providers at different levels:

  • For the whole application when bootstrapping it. All sub Injectors will see the Provider and share the instance associated with. It will always be the same instance.
  • For a specific Component and its sub Components. Other Components won’t see the Provider.
  • For Services. They use one of the Injectors from the element that calls the Service chain.

When using DI with Angular, we will mainly see the “@Injectable” decorator. This decorator marks a class as available to Injector for creation.

In an Angular app, Components consume Services. A Component shouldn’t create a Service. So, we inject the different required Services into the different Components. When Angular creates a new instance of a Component class, it determines which Services or other dependencies that Component needs by looking at the types of its constructor parameters. When Angular discovers that a Component needs a Service, it checks if the Injector already has any existing instances of that same Service. If an instance of that requested Service doesn’t exist, the Injector makes one using the registered Provider and adds it to the Injector before returning the Service to Angular.

A Provider is a recipe for creating a dependency. We must at least register one Provider of any Service we want to use. It can be done in Modules or in Components. Doing this in a Module allows Angular to inject the corresponding Services in any class it creates and so the Service instance lives for the life of the app. By using a Component Provider we restrict the scope of the Service and so it will only be injected into that Component instance or one of its descendant Component instances. It means that Angular can’t inject the same Service instance anywhere else. The lifetime of this Service will also be different: the Service instance will be destroyed when the Component instance is destroyed.

Here is how we can inject a Service in a Component:

import { Injectable } from '@angular/core';
import { Logger } from './logger';
@Injectable()
export class Logger {
log(msg: any) { console.log(msg); }
error(msg: any) { console.error(msg); }
warn(msg: any) { console.warn(msg); }
}

logger.service.ts file

import { Component } from '@angular/core';
import { Logger } from './logger';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
providers: [ Logger ]
})
export class HeroListComponent implements OnInit {
constructor(private logger: Logger {}
}

my-component.component.ts file

We could also do it with the Root Module like so:

@NgModule({
providers: [
Logger
]
})

app.module.ts file

import { Component } from '@angular/core';
import { Logger } from './logger';
@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent implements OnInit {
constructor(private logger: Logger {}
}

my-component.component.ts file

We can also imagine the a Service needs another Service:

import { Injectable } from '@angular/core';
import { Logger } from './logger.service';
@Injectable()
export class MyService {
constructor(private logger: Logger) { }
}

Data Binding

Basically, data bindings allow properties of two objects to be linked so that a change in one causes a change in the other. It establishes a connection between the user interface and the underlying application. It defines a relationship between two objects: a source object that will provide data and a target object that will use the data from the source object. The benefit of data binding is that we no longer have to worry about synchronizing data between our Views and data source.

With Angular, the most common way to display a Component property is to bind that property name through interpolation. Interpolation is the estimation of a value or a set of values based on their context. It allows to evaluate a string containing one or more placeholders and to replace those placeholders with a computed and corresponding value. The context is typically the Component instance. So, basically, in Angular, to achieve this, we have to put the property name in the View enclosed in double curly braces. It will be something like so:

<h1>{{title}}</h1>

Most of the time, bindings are used to connect the visuals of an application with an underlying data model, usually in a realization of the MVVM Pattern (Model-View-ViewModel) or the MVC Pattern (Mode-View-Controller). In Angular, the Component plays the part of the Controller/ViewModel, and the template represents the View.

Angular provides many kinds of data binding. Binding types can be grouped into three categories distinguished by the direction of the data flow: source-to-view, view-to-source and two-way sequence: view-to-source-to-view. When we use binding types other than interpolation, we have to specify a target name that is the name of a property. It looks like an attribute name, but it is not. With data binding, we are not working with HTML attributes, but properties of DOM (Document Object Model) elements. Just to refresh our minds, we may say that attributes are defined by HTML and properties are defined by DOM and the responsibility of HTML attributes is just to initialize DOM properties. Later DOM properties can change, but HTML attributes cannot. Some DOM properties don’t have corresponding attributes and some HTML attributes don’t have corresponding properties. The target of a data binding is something in the DOM.

import { Component } from '@angular/core';@Component({
selector: 'my-component',
templateUrl: './my-component.component.html',
})
export class MyComponent {
imgSrc: String = 'path-to-image';
}

my-component.component.ts file

<img [src]="imgSrc">

my-component.component.html file

We often say that property binding is one-way data binding because it flows a value in one direction, from a Component’s data property into a target element property. However, we are allowed to achieve something called two-way data binding that, for example, lets us display a data property and update that property when the user makes changes. We can do this by using the syntax “[(x)]”.

We are also able to achieve event binding:

export class MyComponent {
doSomething() {
// some code
}
}

my-component.component.ts file

<button (click)="doSomething()">Do something</button>

my-component.component.html file

Input and Output

In a Component, we can use two decorators on properties: “@Input” and “@Output”.

An Input property is a settable property. An Output property is an observable property. Input properties usually receive data values. Output properties expose Event producers.

Declaring an Input property would give something like so:

export class MyComponent {
@Input() name: String;
}

my-component.component.ts file

<my-component name="foo"></my-component>

my-component.component.html file

An Output property almost always returns an Angular EventEmitter. An EventEmitter allows us to emit a custom Event. It is helpful to pass a value to a parent Component. Let’s say that we have something like this:

export class MyComponent {
@Output() deleteItemRequest = new EventEmitter<Item>();
delete() {
this.deleteItemRequest.emit(this.item)
}
}

my-component.component.ts file

<button (click)="delete()">Delete</button>

my-component.component.html file

As we can see, here, we use event binding. So, when the button is clicked, we call the “delete()” method. In the Component, we also declare an Output property that returns an EventEmitter and we declare its underlying type as “Item”. So, when the “delete()” method is called, we use this EventEmitter to emit a new Event. In fact, it will emit an “Item” object.

So, we can now imagine that we have the following thing as a parent Component:

export class ParentComponent {
deleteItem(item: Item) {
// Some code
}
}

parent-component.component.ts file

<parent-component (deleteItemRequest)="deleteItem($event)"></parent-component>

parent-component.component.ts file

When the child Component emits its Event, the parent Component will use the result of this same Event with its own method.

Component Lifecycle Hooks

Angular manages the lifecycle of the different Components. Through different Hooks, it provides a way to perform actions when those different moments occur. We access to those moments by implementing one or more of the lifecycle Hook interfaces in the Angular core library. Each interface has a single Hook method whose name is the interface name prefixed with “ng”.

Down below, we have an example of a Component using the “OnInit” interface:

export class MyComponent implements OnInit {
ngOnInit() {
// Some code
}
}

Communication between parent and child Components

There are a few ways to make a parent and child Component interact. One way is to inject the child Component into the parent as a ViewChild. This could be achieved like so:

import { ViewChild }       from '@angular/core';
import { Component } from '@angular/core';
import { ChildComponent } from './child-component.component';
export class ParentComponent {
@ViewChild(ChildComponent)
private childComponent: ChildComponent;
method1() {
this.childComponent.childMethod1();
}
method2() {
this.childComponent.childMethod2();
}
}

Another way to make a parent and child Component interact is to make them share a Service.

Directives

In Angular, there are three kinds of Directives:

  • ComponentsDirectives with a template
  • Structural Directives — change the DOM layout by adding and removing DOM elements
  • Attribute Directives — change the appearance or behavior of an element, Component, or another Directive

We have already seen Components. They are the most common Directives.

Structural Directives change the structure of the View. They are things like “NgFor” or “NgIf”. Here is an example of different Structural Directives:

<div *ngIf="character" class="name">{{character.name}}</div><ul>
<li *ngFor="let character of characters">{{character.name}}</li>
</ul>
<div [ngSwitch]="character?.size">
<app-big-character *ngSwitchCase="'big'" [character]="character"></app-big-character>
<app-medium-character *ngSwitchCase="'medium'" [character]="character"></app-medium-character>
<app-small-character *ngSwitchCase="'small'" [character]="character"></app-small-character>
<app-character *ngSwitchDefault="'small'" [character]="character"></app-character>
</div>

Attribute Directives are used as attributes of elements. They are things like “NgClass” or “NgStyle”. Here is an example of different Attribute Directives:

<div [ngStyle]="currentStyles">
Some content.
</div>
<div [class.error]="hasError">Some error</div>

Let’s make a little side note for the “NgModelDirective that is part of the “FormsModule”. This Directive helps us when we want to display a data property and update that property when the user makes changes through a form. Using this two-way data binding makes this easier. It will map the various fields of our form to our Data Model. It will ensure that the data in the View and the data in our Data Model are synced.

We can use this Directive like so:

export class MyComponent {
name: string;
}

my-component.component.ts file

<input type="text" [(ngModel)]="name" />

my-component.component.html file

We are also able to build Attribute Directives. We just have to create a class annotated with the “@Directive” decorator.

Pipes

Pipes are a way to operate some transformations over data before displaying them. Angular comes with several built-in Pipes. For example, we can have something like so:

<p>The character's birthday is {{ birthday | date:"MM/dd/yy" }}</p>

We are also able to create our own Pipes by using the “@Pipe” decorator and implementing the “PipeTransform” interface. This could be done like so:

import { Pipe, PipeTransform } from '@angular/core';@Pipe({name: 'exponentialStrength'})
export class ExponentialStrengthPipe implements PipeTransform {
transform(value: number, exponent: string): number {
let exp = parseFloat(exponent);
return Math.pow(value, isNaN(exp) ? 1 : exp);
}
}

Observables

Observables provide support for passing messages between Publishers and Subscribers in our application. An Observable can deliver multiple values of any type.

A Publisher must create an Observable instance. This object defines a function that is executed when a consumer calls the “subscribe()” method. This method, called the subscriber function, states how to get data to be published. To execute our Observable, we have to call its “subscribe()” method and pass it an Observer. This object implements the “Observer” interface and is responsible to handle the various notifications from the Observable.

To use Observables, we need to import the RxJS Library. RxJS is a library for reactive programming, which is a declarative programming paradigm where we program with asynchronous data streams. Data streams can be anything and we are so able to listen to them and react accordingly. A stream is a sequence of continuous Events ordered in time and it can emit three different things: a value of some type, an error or a “completed” value. Asynchronously, we capture these different emitted events by defining functions: one that will execute when a value is emitted, another that will execute when an error is emitted and another one that will execute when “completed” is emitted. The action of listening to the stream is named “subscribing”. The various functions we define are the “Observers” while the stream is the “subject” or the “Observale”. This is the Behavioral Design Pattern called the Observer Pattern. We also have to deal with the “Operators” which are the various pure functions, functions that always evaluate the same result value when we give them the same argument value, that will let us work on the emitted values.

This kind of programming is really helpful when we have to deal with various UI Events related to data Events. It helps us to achieve real-time apps.

Let’s imagine that we have a Service that is responsible to fetch users:

import { Observable } from 'rxjs/Rx'
import { Injectable } from '@angular/core'
import { Http, Response } from '@angular/http'
@Injectable()
export class UsersService {
constructor(public http: Http) {} public fetchUsers() {
return this.http.get('/api/users').map((res: Response) => res.json())
}
}

Our method “fetchUsers()” returns an Observable, our subject. So, we can subscribe to our subject like so:

import { Component } from '@angular/core'
import { Observable } from 'rxjs/Rx'
import { UsersService } from './users.service'
import { User } from './user'
@Component({
selector: "my-component",
templateUrl: "./my-component.component.html",
providers: [ UsersService ]
})
export class MyComponent {
public users: Observable<User[]>
constructor(public usersServce: UsersService) {} public ngOnInit() {
this.users = this.UsersService.fetchUsers()
}
}

In our template file, we have to do the following things:

<ul class="user-list" *ngIf="(users | async).length">
<li class="user" *ngFor="let user of users | async">
{{ user.name }}
</li>
</ul>

We may also want to create an Observable from a Promise. We can do it like so:

const data = fromPromise(fetch('/api/endpoint'));

This create an Observable. To subscribe, we have to do the following thing:

data.subscribe({
next(response) { console.log(response); },
error(err) { console.error('Error: ' + err); },
complete() { console.log('Completed'); }
});

Here, we achieve the process of subscription and as we can see, we define the three functions that we talked about a little earlier.

Forms

We can use Angular event bindings to respond to Events that are triggered by user input. For example, we can imagine the following situation:

export class MyComponent {
values = '';
onKey(event: any) {
this.values += event.target.value;
}
}

my-component.component.ts file

<input (keyup)="onKey($event)">
<p>{{values}}</p>

my-component.component.html file

Angular has also a whole “Form” library that helps us with many things. We can, for example, use it to add some validation rules to our forms.

<input id="name" name="name" class="form-control" required minlength="4" [(ngModel)]="user.name" #name="ngModel" ><div *ngIf="name.invalid && (name.dirty || name.touched)" class="alert alert-danger">    <div *ngIf="name.errors.required">
Name is required.
</div>
<div *ngIf="name.errors.minlength">
Name must be at least 4 characters long.
</div>
</div>

Here, we start by defining a input with a few rules. As we can see, we export the “ngModelDirective to achieve two-way data binding. We also export the form control’s state to a local variable “#name”. Then, we check if the control has been touched and we display different errors if there are some.

With Angular, we also have the ability to dynamically generate forms. To achieve this, we have to create objects derived from the base class “QuestionBase” and that represents the various controls of our forms. We can then treat them through a Service that will build the form and return it as a “FormGroup” object.

Routing & Navigation

In Angular, the Router allows navigation from one View to the next. The Router interprets a browser URL to navigate to a client generated View and, if needed, pass optional parameters. The Router can be bound to links or it can be used in response to some actions.

To use the Router correctly, we need to add a “base” element to our “index.html” file. We also need to import the Router Module. In our “app.module.ts” file, we can do the following thing:

import { RouterModule, Routes } from '@angular/router';const appRoutes: Routes = [
{ path: 'characters', component: CharactersComponent },
{ path: 'character/:id', component: CharacterDetailComponent },
{ path: '', redirectTo: '/characters', pathMatch: 'full' },
{ path: '**', component: PageNotFoundComponent }
];
@NgModule({
imports: [
RouterModule.forRoot(appRoutes)
]
})
export class AppModule { }

As we can see, we define our navigation Routes in the array “appRoutes” and we pass this array to the “RouterModule”. We can now use the “RouterOutletDirective, that marks where the Router displays a View, to create some kind of navigation menu:

<nav>
<a routerLink="/characters" routerLinkActive="active">Characters</a>
</nav>
<router-outlet></router-outlet>

After the end of each successful navigation lifecycle, the Router builds a tree of “ActivatedRoute” objects that make up the current state of the Router. We are able to access the current “RouterState” from anywhere in the application using the Router Service and the “routerState” property.

Conclusion

Through this article, we got a brief overview of the Angular technology. It was more a theoretical post than a practical example. Of course, we didn’t cover entirely each subject and there are plenty of other subjects that we could have explored like Unit Testing or E2E Testing. Now, however, we have enough knowledge of Angular to start a project and to dig deeper into this framework.

One last word

If you like this article, you can consider supporting and helping me on Patreon! It would be awesome! Otherwise, you can find my other posts on Medium and Tumblr. You will also know more about myself on my personal website. Until next time, happy headache!

--

--