Creating an Angular Application

Generating the Application using ng new

Angular uses the ng tool to generate all sorts of code for you. In fact ng is used to generate all of the angular items from components to the entire application.

First install the current long term support version of npm:

nvm install --lts
nvm use node --lts

Alternatively use the latest release:

$ nvm ls
v8.11.2
        v8.12.0
         v9.3.0
       v10.14.2
       v10.15.3
       v10.16.0
        v11.4.0
        v12.0.0
        v12.2.0
       v12.13.1
       v13.11.0
       v14.17.0
->     v14.17.3
        v16.1.0
         system

Now install and use the lastest version:

$ nvm install v16.1.0
$ nvm use v16.1.0

ng is added to your system by installing the Angular cli globally using the node package manager npm:

npm install -g @angular/cli

You can check the angular CLI version:

ng version

At the time of this writing, the version Angular CLI: 12.1.3 is current.

The global ng installation is used to generate a new application:

ng new <APPLICATION_NAME> --style=scss --routing

This will create a folder called <APPLICATION_NAME> in the current working folder containing the new project. It will use the sass processor for CSS stylesheets using the scss syntax. It will automatically use routing.

You can also go through a interactive process where the angular CLI asks you about all options before creating the project.

ng new <APPLICATION_NAME> --interactive

Once a project was generated using the global version of the angular CLI, you change into the project folder and from there on out, the local version of the angular CLI as specified in the package.json is used. This means even when the global Angular CLI is updated to a newer version, your application will not break because it locally uses the version as specified in the package.json file.

This helps project stability as you can update the global angular CLI version for new projects and keep the local angular CLI version to prevent the angular application from breaking due to version differences.

Additional Dependencies

For the NgRx store architecture:

npm install @ngrx/store --save
npm install @ngrx/effects --save

Starting the Application

You can open the application folder using Visual Studio Code. Withing Visual Studio Code, open a terminal using the Terminal menu item in the menu bar and the New Terminal Item within it.

To start the application, type npm start which will internally call ng serve.

npm run start

You can also call ng serve directly

ng serve

The application is running at http://localhost:4200/

Adding a Module

Angular is particularly useful because it has a rigid scheme for organizing code which benefits structure and ultimately the quality your application code will have in the long run.

That scheme consists of the use of Typescript Modules which contain components.

There are two types of modules: modules and feature modules.

Initially, ng generates a module which is the app module containing the app component, which is used as a starting point for the application (called bootstrapping).

Starting from the main property inside the angular.json file, a main.ts typescript file is configured. WebPack will execute this main.ts / main.js file when starting the application.

Inside main.ts, a call to bootstrapModule() is made and the app module (AppModule) is specified as a parameter.

Looking into app/app.module.ts, you can see the bootstrap property of the @NgModule decorator. It contains the AppComponent. That means, when angular starts the app module, it will initialize the AppComponent first and use it as an entry point into the application.

Feature modules can be added to the application to extent the application with new functionality. For each separate feature, you create a new feature module so that the feature modules stay independent from each other (strong cohesion, weak coupling). A module will contain all components, services, interceptors, decorators, pipes, model classes and everything else to make a feature work as a independent unit.

Move common functionality such as utilities into their own modules to reuse them from several modules.

The angular CLI allows you to add a module:

ng generate module <MODULE_NAME>

To generate a module that houses all user-related code, execute

ng generate module user

Note that the name was choosen to be user and not user-module or anything. Angular CLI will automatically generate a user folder and a user.module.ts file. The CLI will postfix the module identifier to the generated files for you!

Lazy Loaded Feature Modules

Documentation is: https://angular.io/guide/lazy-loading-ngmodules

A word of advice: lazy loaded feature modules have been updated in newer versions of angular. This article shows console output from version 12. If your output differs, consider migrating to the newest angular version. 

When you want a lazy loaded module, do not import the module into the app module, instead use the router. The idea behind a lazy loaded module is to only load it into memory, when the router navigates to the module.

Therefore the router is used to load the module when a certain route is visited. To setup the lazy loading, update the file app-routing.module.ts

const routes: Routes = [
{
path: 'projects',
loadChildren: () =>
import('./projects/projects.module').then((mod) => mod.ProjectsModule),
},
];

Here, once the path ‘projects’ is followed, the router will execute the import() function which loads the lazy loaded projects module in this case.

The question is, which component will be rendered when the path is visited? The code in the app-routing.module.ts file does not specify a component to load within the projects module! The projects module itself will again contain a routing configuration file called projects-routing.module.ts which specifies all the routes and components.

The file looks like this:

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { ProjectContainerComponent } from './components/project-container/project-container.component';

const routes: Routes = [
{
path: '',
component: ProjectContainerComponent,
},
];

@NgModule({
imports: [RouterModule.forChild(routes)],
exports: [RouterModule],
})
export class ProjectsRoutingModule {}

One last change is necessary, in the lazy loaded feature module, import the ProjectsRoutingModule from the projects-routing.module.ts file and add it to the imports of the FeatureModule so it partakes in the routing:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProjectContainerComponent } from './components/project-container/project-container.component';

import { ProjectsRoutingModule } from './projects-routing.module';

@NgModule({
declarations: [
ProjectContainerComponent
],
imports: [
CommonModule, ProjectsRoutingModule
]
})
export class ProjectsModule { }

When you start your app, you should see a listing of Chunks. Chunks are the files containing the application code, that are ultimately downloaded to the client to run your app. You should see your lazy loaded module listed as a Lazy Chunk File as opposed to the Initital Chunk File option for eagerly loaded modules.

Initial Chunk Files                    | Names         |      Size
vendor.js                              | vendor        |   2.39 MB
polyfills.js                           | polyfills     | 128.55 kB
runtime.js                             | runtime       |  12.51 kB
main.js                                | main          |   9.50 kB
styles.css                             | styles        | 118 bytes

                                       | Initial Total |   2.54 MB

Lazy Chunk Files                       | Names         |      Size
src_app_projects_projects_module_ts.js | -             |   5.81 kB

Adding a Service

To add a service into a module, you can use the Angular CLI.

ng generate module auth
cd src/app/module
ng generate service services/auth

This will create the auth.service.ts file inside the auth module.

import { Injectable } from '@angular/core';

@Injectable({
providedIn: 'root'
})
export class AuthService {

constructor() { }
}

@Injectable providedIn: root means that, the provider for this service is added to the root provider. This is documented here: https://angular.io/guide/providers

A provider is responsible to retrieve an instance for a dependency used in dependency injection.

Building an Angular Example App

We will build an application called ‘cash’ that helps you organize your finances. The code is available on github.

The cash application manages accounts.

Real Accounts mirror an existing account that you own within your bank institution.

Virtual Accounts are accounts that you can create within cash and which do not exist in your bank institution.

Virtual Accounts can have zero or one real accounts connected to them.
That means virtual accounts can exist, that are not backed by a real account. A real account can be connected to at most one virtual account.
That means it is not possible to connect a real account to two virtual accounts.

If a virtual account is connected to a real account, the real account is hidden by the virtual account. The amount of money that is stored in the real account is now accesseable only via it’s virtual account.

The way the cash application is structured is that whenever you add a real account to the cash application, cash will automatically create a virtual account for you and connect it to the real account.

You basically only are working with virtual accounts when you use the cash application. The layer of virtual accounts hides the layer of real accounts beneath it. You only interface with virtual accounts.

You can create any number of virtual accounts. For example if you want to structure your income and if you want to save up some money for a new PC or anything, you can create a virtual account for your savings and transfer money in between virtual accounts onto your savings account.

That way your income is subtracted by the amount of money you want to save and you can clearly see how much money there is left to spend after putting away the saved amount of money.

Money is transferred between accounts in transactions. A transaction starts at at most one virtual account and ends at at most another virtual account. A transaction transfers a non-negative amount of money and has a date as well as a description. Transaction between the same virtual accounts are not allowed.

For a transaction, there are the following cases:
A) Source Virtual Account (SVA) and Target Virtual Account (TVA) both are not backed by real accounts.
B) SVA is backed by a real account but TVA is not.
C) SVA is not backed by a real account but TVA is.
D) SVA and TVA are both backed by real accounts.

There are special cases:

Incoming amounts of money are transactions without a source account.
Expenses are transactions that have no target.

E) There is no SVA. The transaction adds income to your bank accounts.
F) There is no TVA. The transaction denotes an expense you made.

If SVA and TVA are both backed by real accounts (case D), then the money of the transaction is also transferred between the real accounts.

If SVA and TVA are both not backed (case A) the real accounts are not altered.

If there is a sequence of transactions of cases B – A* – C, then the money that made it from the real source account to the real target account is also transferred between the real accounts.

B – A* – C means that the sequence starts with a transaction of type B, then there are arbitrary many transactions of type A, then the sequence ends with a transaction of type C. That means, money travels over several hops over virtual accounts and in a global perspective between real accounts.

The amount of money in an account over a time frame can be displayed as a graph.

Transactions are stored in a transaction log which you can open and inspect at any time.

After every transaction, ‘cash’ will compute the amount of money in all real accounts and the money in all virtual accounts. The two amounts of money have to be the same at all times.

Let’s implement this as an angular application. It might be a bit ambitious but it is a usefull example that you can really make use of in your real life.

CRUD for Accounts

The first step will be CRUD for accounts. CRUD stands for Create, Retrieve, Update and Delete. I will use the acronym for an application that provides a user interface and backend code that allows the user to manage items (Create, Retrieve, Update and Delete). The user interface will consist of forms that allow the user to input data for accounts or edit existing accounts. The backend code will persist the accounts to a SQL datastore using Express and the excellent Sequelize SQL-ORM mapper.

First a SQL schema is created to manage real and virtual accounts. Then an API is created for the account CRUD operations in the backend. Once the backend and data storage are functional, a Form is needed to add new accounts. A list is needed to display existing accounts for editing, inspection and deletion.

Accessing Virtual Account via the API

First, lets create a service that covers the API calls. Creating services is done via the angular cli.

ng generate service <service_name>
ng generate service Account
ng generate service AccountDetails

If you want an AccountService, you have to generate with the name ‘Account’. ng will add the name service for you.

Leave a Reply