Translate an Angular 8 application using NGX-translate

Translate an Angular 8 application using NGX-translate

How to Translate in Angular 8 Application using NGX-translate

How To Translate Angular 8 Application Using ngx-translate. In this article, we will discuss Internationalization in Angular 8 using ngx-translate library.

NGX-Translate is an internationalization library for Angular. Internationalization is the process of translating an application into multiple languages. Using this library, we can translate our application language into multiple languages. This will work with not only static data, but dynamic data as well.

Angular is a TypeScript-based, open source web application framework developed by Google. Angular is a platform for building mobile, desktop, and web applications.

Step 1

Create a new Angular project by using the following command. First, we need to set a path for creating a new angular app.

ng new MultilanguageApp

Creating a new Angular project

Now, open this project in Visual Studio Code. To open this in Visual Studio Code, choose the project path and run below command.

code .

Step 2

Now, install the ngx-translate library by using the following commands. We can install bootstrap and jquery for design.

npm install @ngx-translate/core --save

npm install @ngx-translate/http-loader --save

npm install [email protected] jquery –save

Example of how to run the previous commands:

Output of previous commands

After installing the bootstrap package, we have to add reference of the CSS file on the styles.css file. To do this, fun the following command:

@import "~bootstrap/dist/css/bootstrap.css";

Step 3

Import the necessary modules into app.module.ts.

Installing necessary modules

Step 4

Now, expand the src folder and right-click on the Assets folder. Add a new folder under it and rename that to "i18" and add JSON files to this folder (based on how many languages you want to translate. I'm showing an example of two language English and French so I'm creating two files).

  1. en.json
  2. fr.json

The JSON file is a combination of a key-value pair.

Examples:

Example of JSON file

Example of key-value pairs

Step 5

Open the en.json file and paste the following code

{  
    "Addemployee": "Add-employee",  
    "Name": "Name",  
    "Email": "Email",  
    "PhoneNo": "Phone No",  
    "Submit": "Submit",  
    "Cancel": "Cancel",  
    "Home": "Home",  
    "Employee": "Employee",  
    "EmployeeList": "Employee List"  
}  
Step 6

Open fr.json file and paste the following code.

{  
    "Addemployee": "Ajouter employé",  
    "Name": "prénom",  
    "Email": "Email",  
    "PhoneNo": "Pas de téléphone",  
    "Submit": "Soumettre",  
    "Cancel": "Annuler",  
    "Home": "Accueil",  
    "Employee": "Employée",  
    "EmployeeList": "Liste des employés"  
}  
Step 7

Open the app.component.html file and paste the following code.

<!--The content below is only a placeholder and can be replaced.-->  
<div class="bg-dark" style="text-align:center;color: #fff">  
  <h2 class="navbar-brand ">  
    Welcome to {{ title }}!  
  </h2>  
  <select #langSelect (change)="changeLang(langSelect.value)">  
      <option *ngFor="let lang of translate.getLangs()" [value]="lang" [selected]="lang === translate.currentLang">{{ lang }}</option>  
  </select>  
</div>  
<div class="row">  
  <div class="col-md-2">  
    <ul class="list-group">  
      <li class="list-group-item"><a [routerLink]="['/']">{{ 'Home' | translate }}</a></li>  
      <li class="list-group-item"><a [routerLink]="['/employee']">{{ 'Employee' | translate }}</a></li>  
      <li class="list-group-item"><a [routerLink]="['/employeelist']">{{ 'EmployeeList' | translate }}</a></li>  
      <!-- <li class="list-group-item"><a href="employeelist">EmployeeList(Reload)</a></li> -->  
    </ul>  
  </div>  
  <div class="col-md-8">  
    <router-outlet></router-outlet>  
  </div>  
</div>

Ex.

Declaring key and translating text

Step 8

Open the app.component.ts file and paste the following code.

import { Component } from '@angular/core';  
import { TranslateService } from '@ngx-translate/core';  

@Component({  
  selector: 'app-root',  
  templateUrl: './app.component.html',  
  styleUrls: ['./app.component.css']  
})  
export class AppComponent {  
  title = 'MultilanguageApp';  

  constructor(  
    public translate: TranslateService) {  
    translate.addLangs(['en', 'fr']);  
    if (localStorage.getItem('locale')) {  
      const browserLang = localStorage.getItem('locale');  
      translate.use(browserLang.match(/en|fr/) ? browserLang : 'en');  
    } else {  
      localStorage.setItem('locale', 'en');  
      translate.setDefaultLang('en');  
    }  
  }  
  changeLang(language: string) {  
    localStorage.setItem('locale', language);  
    this.translate.use(language);  
  }  
} 

Ex.

Import service on constructor

Step 9

Run this command for hosting the application with default port 4200.

Running the command on port 4200

Output Window

Final output

Summary

In this article, we discussed ngx-translate with a demo example.

NGX-Translate is an internationalization library for Angular. Internationalization is the process of translating our application into multiple languages. In my next article, I will discuss an Angular i18n language translator and Paypal subscription payment integration using Angular 8. Any feedback related to this article is most welcome!

Angular 9 Tutorial: Learn to Build a CRUD Angular App Quickly

What's new in Bootstrap 5 and when Bootstrap 5 release date?

What’s new in HTML6

How to Build Progressive Web Apps (PWA) using Angular 9

What is new features in Javascript ES2020 ECMAScript 2020

Angular 7 CRUD with Nodejs and MySQL Example

Angular 7 CRUD with Nodejs and MySQL Example

Angular7 CRUD with nodejs and mysql example - Hey there, Today we will proceed to create a demo for CRUD with Mysql, Express, Angular7(MEAN) and Nodejs from scratch using Angular CLI

Below are the requirements for creating the CRUD on MEAN

  • Node.js
  • Angular CLI
  • Angular 7
  • Mysql
  • IDE or Text Editor

We assume that you have already available the above tools/frameworks and you are familiar with all the above that what individually actually does.

So now we will proceed step by step to achieve the task.

1. Update Angular CLI and Create Angular 7 Application

At first, We have to update the Angular CLI to the latest version. Open the terminal then go to the project folder and then type the below command to update the Angular CLI

sudo npm install -g @angular/cli

Once the above task finishes, Next task is to create new angular application with below command. So go to your project folder and then type below command:

ng new angular7-crud

then go to the newly created folder of angular application with cd /angular7-crud  and type **ng serve. **Now, open the browser then go to http://localhost:4200 you should see this page.

2. Create a server with node.js express and Mysql for REST APIs

create a separate folder named server for server-side stuff, Then move inside folder and create server.js by typing touch server.js

Let’s have a look on the server.js file

let app = require('express')(),
server = require('http').Server(app),
bodyParser = require('body-parser')
express = require('express'),
cors = require('cors'),
http = require('http'),
path = require('path');
 
let articleRoute = require('./Routes/article'),
util = require('./Utilities/util');
 
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false }));
 
app.use(cors());
 
app.use(function(err, req, res, next) {
return res.send({ "statusCode": util.statusCode.ONE, "statusMessage": util.statusMessage.SOMETHING_WENT_WRONG });
});
 
app.use('/article', articleRoute);
 
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next();
});
 
/*first API to check if server is running*/
app.get('*', (req, res) => {
res.sendFile(path.join(__dirname, '../server/client/dist/index.html'));
})
 
 
server.listen(3000,function(){
console.log('app listening on port: 3000');
});

In the above file we can see, at the top, there are required packages for the app. Below that body parsing, middleware and routing is done.

The next task is to create routes and create a file article.js . So creating a folder name ‘Routes’ and adding article.js within it.

Add the below code for routing in article.js inside routing folder

let express = require('express'),
router = express.Router(),
util = require('../Utilities/util'),
articleService = require('../Services/article');
 
/**Api to create article */
router.post('/create-article', (req, res) => {
articleService.createArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to update article */
router.put('/update-article', (req, res) => {
articleService.updateArticle(req.body, (data) => {
res.send(data);
});
});
 
// /**Api to delete the article */
router.delete('/delete-article', (req, res) => {
articleService.deleteArticle(req.query, (data) => {
res.send(data);
});
});
 
/**Api to get the list of article */
router.get('/get-article', (req, res) => {
documentService.getArticle(req.query, (data) => {
res.send(data);
});
});
 
// /**API to get the article by id... */
router.get('/get-article-by-id', (req, res) => {
articleService.getArticleById(req.query, (data) => {
res.send(data);
});
});
 
module.exports = router;

Now create a folder named Utilities for all config, common methods and mysql connection config.

Now I am adding config values in a file named config.js

let environment = "dev";
 
let serverURLs = {
"dev": {
"NODE_SERVER": "http://localhost",
"NODE_SERVER_PORT": "3000",
"MYSQL_HOST": 'localhost',
"MYSQL_USER": 'root',
"MYSQL_PASSWORD": 'password',
'MYSQL_DATABASE': 'demo_angular7_crud',
}
}
 
let config = {
"DB_URL_MYSQL": {
"host": `${serverURLs[environment].MYSQL_HOST}`,
"user": `${serverURLs[environment].MYSQL_USER}`,
"password": `${serverURLs[environment].MYSQL_PASSWORD}`,
"database": `${serverURLs[environment].MYSQL_DATABASE}`
},
"NODE_SERVER_PORT": {
"port": `${serverURLs[environment].NODE_SERVER_PORT}`
},
"NODE_SERVER_URL": {
"url": `${serverURLs[environment].NODE_SERVER}`
}
};
 
module.exports = {
config: config
};

Now configure mysql connection. So I am writing the connection with database in a separate file. So creating a file named mysqkConfig.js under Utilities folder and adding the below line of code for mysql connection:

var config = require("../Utilities/config").config;
var mysql = require('mysql');
var connection = mysql.createConnection({
host: config.DB_URL_MYSQL.host,
user: config.DB_URL_MYSQL.user,
password: config.DB_URL_MYSQL.password,
database: config.DB_URL_MYSQL.database,
});
 
connection.connect(() => {
require('../Models/Article').initialize();
});
 
let getDB = () => {
return connection;
}
 
module.exports = {
getDB: getDB
}

Now I am creating separate file name util.js to save common methods and common status code/message:

// Define Error Codes
let statusCode = {
OK: 200,
FOUR_ZERO_FOUR: 404,
FOUR_ZERO_THREE: 403,
FOUR_ZERO_ONE: 401,
FIVE_ZERO_ZERO: 500
};
 
// Define Error Messages
let statusMessage = {
SERVER_BUSY : 'Our Servers are busy. Please try again later.',
DATA_UPDATED: 'Data updated successfully.',
DELETE_DATA : 'Delete data successfully',
 
};
 
module.exports = {
statusCode: statusCode,
statusMessage: statusMessage
}

Now the next part is model, So create a folder named Models and create a file **Article.js **and add the below code in it:

let mysqlConfig = require("../Utilities/mysqlConfig");
 
let initialize = () => {
mysqlConfig.getDB().query("create table IF NOT EXISTS article (id INT auto_increment primary key, category VARCHAR(30), title VARCHAR(24))");
 
}
 
module.exports = {
initialize: initialize
}

Now create DAO folder and add a file articleDAO.js for writting the mysql queries common functions:

let dbConfig = require("../Utilities/mysqlConfig");


 
let getArticle = (criteria, callback) => {
//criteria.aricle_id ? conditions += ` and aricle_id = '${criteria.aricle_id}'` : true;
dbConfig.getDB().query(`select * from article where 1`,criteria, callback);
}
 
let getArticleDetail = (criteria, callback) => {
    let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dbConfig.getDB().query(`select * from article where 1 ${conditions}`, callback);
}
 
let createArticle = (dataToSet, callback) => {
console.log("insert into article set ? ", dataToSet,'pankaj')
dbConfig.getDB().query("insert into article set ? ", dataToSet, callback);
}
 
let deleteArticle = (criteria, callback) => {
let conditions = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
console.log(`delete from article where 1 ${conditions}`);
dbConfig.getDB().query(`delete from article where 1 ${conditions}`, callback);
 
}
 
let updateArticle = (criteria,dataToSet,callback) => {
    let conditions = "";
let setData = "";
criteria.id ? conditions += ` and id = '${criteria.id}'` : true;
dataToSet.category ? setData += `category = '${dataToSet.category}'` : true;
dataToSet.title ? setData += `, title = '${dataToSet.title}'` : true;
console.log(`UPDATE article SET ${setData} where 1 ${conditions}`);
dbConfig.getDB().query(`UPDATE article SET ${setData} where 1 ${conditions}`, callback);
}
module.exports = {
getArticle : getArticle,
createArticle : createArticle,
deleteArticle : deleteArticle,
updateArticle : updateArticle,
getArticleDetail : getArticleDetail
}

Now one create Services folder and add a file article.js for all the logic of API

let async = require('async'),
parseString = require('xml2js').parseString;
 
let util = require('../Utilities/util'),
articleDAO = require('../DAO/articleDAO');
//config = require("../Utilities/config").config;
 
 
/**API to create the atricle */
let createArticle = (data, callback) => {
async.auto({
article: (cb) => {
var dataToSet = {
"category":data.category?data.category:'',
"title":data.title,
}
console.log(dataToSet);
articleDAO.createArticle(dataToSet, (err, dbData) => {
if (err) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
 
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });
});
}
//]
}, (err, response) => {
callback(response.article);
});
}
 
/**API to update the article */
let updateArticle = (data,callback) => {
async.auto({
articleUpdate :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
console.log('phase 1');
var criteria = {
id : data.id,
}
var dataToSet={
"category": data.category,
"title":data.title,
}
console.log(criteria,'test',dataToSet);
                    articleDAO.updateArticle(criteria, dataToSet, (err, dbData)=>{
                        if(err){
cb(null,{"statusCode":util.statusCode.FOUR_ZERO_ONE,"statusMessage":util.statusMessage.SERVER_BUSY});
                        return; 
                        }
                        else{
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DATA_UPDATED,"result":dataToSet });                        
                        }
                    });
}
}, (err,response) => {
callback(response.articleUpdate);
});
}
 
/**API to delete the subject */
let deleteArticle = (data,callback) => {
console.log(data,'data to set')
async.auto({
removeArticle :(cb) =>{
if (!data.id) {
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.PARAMS_MISSING })
return;
}
var criteria = {
id : data.id,
}
articleDAO.deleteArticle(criteria,(err,dbData) => {
if (err) {
console.log(err);
cb(null, { "statusCode": util.statusCode.FOUR_ZERO_ONE, "statusMessage": util.statusMessage.SERVER_BUSY });
return;
}
cb(null, { "statusCode": util.statusCode.OK, "statusMessage": util.statusMessage.DELETE_DATA });
});
}
}, (err,response) => {
callback(response.removeArticle);
});
}
 
/***API to get the article list */
let getArticle = (data, callback) => {
async.auto({
article: (cb) => {
articleDAO.getArticle({},(err, data) => {
if (err) {
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
/***API to get the article detail by id */
let getArticleById = (data, callback) => {
async.auto({
article: (cb) => {
let criteria = {
"id":data.id
}
articleDAO.getArticleDetail(criteria,(err, data) => {
if (err) {
console.log(err,'error----');
cb(null, {"errorCode": util.statusCode.INTERNAL_SERVER_ERROR,"statusMessage": util.statusMessage.SERVER_BUSY});
return;
}
cb(null, data[0]);
return;
});
}
}, (err, response) => {
callback(response.article);
})
}
 
module.exports = {
createArticle : createArticle,
updateArticle : updateArticle,
deleteArticle : deleteArticle,
getArticle : getArticle,
getArticleById : getArticleById
};

3. Create angular component for performing CRUD task of article

ng g component article

Above command will generate all required files for build article component and also automatically added this component to app.module.ts.

create src/app/article/article.component.css (0 bytes)
create src/app/article/article.component.html (23 bytes)
create src/app/article/article.component.spec.ts (614 bytes)
create src/app/article/article.component.ts (321 bytes)
update src/app/app.module.ts (390 bytes)

Now we need to add HttpClientModule to app.module.ts. Open and edit src/app/app.module.ts then add this import. And add it to @NgModule imports after BrowserModule. Now our app.module.ts will have following code:

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
 
import { AppComponent } from './app.component';
import { ArticleComponent } from './article.component';
import { ArticleService } from './article.service';
 
@NgModule({
imports: [
BrowserModule,
HttpModule,
ReactiveFormsModule
],
declarations: [
AppComponent,
ArticleComponent
],
providers: [
ArticleService
],
bootstrap: [
AppComponent
]
})
export class AppModule { }

Now create a service file where we will make all the request to the server for CRUD operation. Command for creating service is ng g service artcle , for now I have just created a file named it article.service.ts. Let's have a look in the code inside this file.

import { Injectable } from '@angular/core';
import { Http, Response, Headers, URLSearchParams, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
 
import { Article } from './article';
 
@Injectable()
export class ArticleService {
//URL for CRUD operations
    articleUrl = "http://localhost:3000/article";
    //Create constructor to get Http instance
    constructor(private http:Http) {
    }
    
    //Fetch all articles
getAllArticles(): Observable<Article[]> {
return this.http.get(this.articleUrl+"/get-article")
              .map(this.extractData)
         .catch(this.handleError);
 
}
    //Create article
createArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
let options = new RequestOptions({ headers: cpHeaders });
return this.http.post(this.articleUrl+"/create-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
    //Fetch article by id
getArticleById(articleId: string): Observable<Article> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        console.log(this.articleUrl +"/get-article-by-id?id="+ articleId);
        return this.http.get(this.articleUrl +"/get-article-by-id?id="+ articleId)
             .map(this.extractData)
             .catch(this.handleError);
}   
    //Update article
updateArticle(article: Article):Observable<number> {
     let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
return this.http.put(this.articleUrl +"/update-article", article, options)
.map(success => success.status)
.catch(this.handleError);
}
//Delete article    
deleteArticleById(articleId: string): Observable<number> {
        let cpHeaders = new Headers({ 'Content-Type': 'application/json' });
        let options = new RequestOptions({ headers: cpHeaders });
        return this.http.delete(this.articleUrl +"/delete-article?id="+ articleId)
             .map(success => success.status)
             .catch(this.handleError);
}   
    private extractData(res: Response) {
        let body = res.json();
return body;
}
private handleError (error: Response | any) {
        console.error(error.message || error);
        return Observable.throw(error.status);
}
}

In the above file we have made all the http request for the CRUD operation. Observables of rxjs library has been used to handle the data fetching from http request.

Now let's move to the next file, article.component.ts. Here we have all the login part of the app. Let's have a look code inside this file:

import { Component, OnInit } from '@angular/core';
import { FormControl, FormGroup, Validators } from '@angular/forms';
 
import { ArticleService } from './article.service';
import { Article } from './article';
 
@Component({
selector: 'app-article',
templateUrl: './article.component.html',
styleUrls: ['./article.component.css']
})
export class ArticleComponent implements OnInit {
//Component properties
allArticles: Article[];
statusCode: number;
requestProcessing = false;
articleIdToUpdate = null;
processValidation = false;
//Create form
articleForm = new FormGroup({
title: new FormControl('', Validators.required),
category: new FormControl('', Validators.required)   
});
//Create constructor to get service instance
constructor(private articleService: ArticleService) {
}
//Create ngOnInit() and and load articles
ngOnInit(): void {
     this.getAllArticles();
}
//Fetch all articles
 
getAllArticles() {
        this.articleService.getAllArticles()
         .subscribe(
data => this.allArticles = data,
                errorCode => this.statusCode = errorCode);
                
}
//Handle create and update article
onArticleFormSubmit() {
     this.processValidation = true;
     if (this.articleForm.invalid) {
     return; //Validation failed, exit from method.
     }
     //Form is valid, now perform create or update
this.preProcessConfigurations();
     let article = this.articleForm.value;
     if (this.articleIdToUpdate === null) {
     //Generate article id then create article
this.articleService.getAllArticles()
     .subscribe(articles => {
            
         //Generate article id    
         let maxIndex = articles.length - 1;
         let articleWithMaxIndex = articles[maxIndex];
         let articleId = articleWithMaxIndex.id + 1;
         article.id = articleId;
         console.log(article,'this is form data---');
         //Create article
    this.articleService.createArticle(article)
             .subscribe(successCode => {
                    this.statusCode = successCode;
                    this.getAllArticles();  
                    this.backToCreateArticle();
                 },
                 errorCode => this.statusCode = errorCode
             );
         });        
     } else {
  //Handle update article
article.id = this.articleIdToUpdate;        
     this.articleService.updateArticle(article)
     .subscribe(successCode => {
         this.statusCode = successCode;
                 this.getAllArticles();  
                    this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);  
     }
}
//Load article by id to edit
loadArticleToEdit(articleId: string) {
this.preProcessConfigurations();
this.articleService.getArticleById(articleId)
     .subscribe(article => {
            console.log(article,'poiuytre');
         this.articleIdToUpdate = article.id;
                    this.articleForm.setValue({ title: article.title, category: article.category });
                    this.processValidation = true;
                    this.requestProcessing = false;
         },
         errorCode => this.statusCode = errorCode);
}
//Delete article
deleteArticle(articleId: string) {
this.preProcessConfigurations();
this.articleService.deleteArticleById(articleId)
     .subscribe(successCode => {
         //this.statusCode = successCode;
                    //Expecting success code 204 from server
                    this.statusCode = 204;
                 this.getAllArticles();  
                 this.backToCreateArticle();
             },
         errorCode => this.statusCode = errorCode);
}
//Perform preliminary processing configurations
preProcessConfigurations() {
this.statusCode = null;
     this.requestProcessing = true;
}
//Go back from update to create
backToCreateArticle() {
this.articleIdToUpdate = null;
this.articleForm.reset(); 
     this.processValidation = false;
}
}

Now we have to show the task over browser, So lets have a look inside article.component.html file.

<h1 class="text-center">Angular 7 CRUD Demo App</h1>
<h3 class="text-center" *ngIf="articleIdToUpdate; else create">
Update Article for Id: {{articleIdToUpdate}}
</h3>
<ng-template #create>
<h3 class="text-center"> Create New Article </h3>
</ng-template>
<div>
<form [formGroup]="articleForm" (ngSubmit)="onArticleFormSubmit()">
<table class="table-striped" style="margin:0 auto;">
<tr><td>Enter Title</td><td><input formControlName="title">
   <label *ngIf="articleForm.get('title').invalid && processValidation" [ngClass] = "'error'"> Title is required. </label>
 </td></tr>
<tr><td>Enter Category</td><td><input formControlName="category">
   <label *ngIf="articleForm.get('category').invalid && processValidation" [ngClass] = "'error'"> Category is required. </label>
  </td></tr>  
<tr><td colspan="2">
   <button class="btn btn-default" *ngIf="!articleIdToUpdate">CREATE</button>
    <button class="btn btn-default" *ngIf="articleIdToUpdate">UPDATE</button>
   <button (click)="backToCreateArticle()" *ngIf="articleIdToUpdate">Go Back</button>
  </td></tr>
</table>
</form>
<br/>
<div class="text-center" *ngIf="statusCode; else processing">
<div *ngIf="statusCode === 201" [ngClass] = "'success'">
   Article added successfully.
</div>
<div *ngIf="statusCode === 409" [ngClass] = "'success'">
Article already exists.
</div>   
<div *ngIf="statusCode === 200" [ngClass] = "'success'">
Article updated successfully.
</div>   
<div *ngIf="statusCode === 204" [ngClass] = "'success'">
Article deleted successfully.
</div>   
<div *ngIf="statusCode === 500" [ngClass] = "'error'">
Internal Server Error.
</div> 
</div>
<ng-template #processing>
  <img *ngIf="requestProcessing" src="assets/images/loading.gif">
</ng-template>
</div>
<h3 class="text-center">Article List</h3>
<table class="table-striped" style="margin:0 auto;" *ngIf="allArticles">
<tr><th> Id</th> <th>Title</th><th>Category</th><th></th><th></th></tr>
<tr *ngFor="let article of allArticles" >
<td>{{article.id}}</td> <td>{{article.title}}</td> <td>{{article.category}}</td>
  <td><button class="btn btn-default" type="button" (click)="loadArticleToEdit(article.id)">Edit</button> </td>
  <td><button class="btn btn-default" type="button" (click)="deleteArticle(article.id)">Delete</button></td>
</tr>
</table>

Now since I have created server and client two separate folder for nodejs and angular task. So will run both the apps with npm start over two tabs of terminal.

On the browser, over link http://localhost:4200. App will look like below

That’s all for now. Thank you for reading and I hope this post will be very helpful for creating CRUD operations with angular7,node.js & mysql.

================================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

What is Latest Features of Angular.js?

What is Latest Features of Angular.js?

In this post, we discuss the latest features in Angular.js, including differential loading. Go in-depth on what's new in Angular.

The latest version of Angular was slated to release in March-April 2019. But, it eventually happened in 2019. Yes, we are talking about Angular 8 — the latest version of Angular.js.

However, Angular 7.0 is supported until April 2022. So, it becomes imperative for organizations to explore the new features incorporated in Angular 8 and decide on embracing Angular 8 whether or not they should upgrade from Angular 7.0.

New Features of Angular 8.0

Differential Loading of JavaScript

Differential loading enables browsers to select optimized or legacy bundles according to their capabilities and then automatically load the correct one. Users then receive the bundle(s) they require based on their specific needs.

In the latest version, Angular serves different browsers with different bundles, and this is by default. The CLI extensions generate different bundles for old legacy ES5 browsers and modern JavaScript (ES2015+) browsers.

Moreover, it enhances the loading speed and the time to interactive (TTI) for a modern browser. The speed of applications and building a process takes place by using polyfills or modern syntax. Moreover, differential loading also helps in saving 7 to 20 percent of bundle size on average.

Differential loading in Angular 8.0 forms a part of Manfred Steyer's project, ngx-build-modern. All bundling is done with the ng build command and the -prod extension, without requiring special actions.

Opt-In Usage Sharing

It acts as an addition to the Angular CLI that aligns Angular 8 with community needs. Angular needs to keep a tab on how developers use the platform and what improvements are required. This is done with the information on the command used and builds speed. You can share telemetry on CLI usage with your development team.

Angular emphasizes consent on data sharing, unlike other platforms that collect data by default on an opt-out basis. It stops when you command them not to. Others don’t even allow them to ask them to share telemetry.

Improved Web Worker Bundling

With CPU-intensive tasks, the best way to speed up the application and improve parallelizability is web workers. Web workers write code off the main thread and offload tasks to a background thread.

The code running in the web worker cannot exist in the same JavaScript file in the application. These two must be different. Those working with tools such as Angular CLI bundles JavaScript automatically into a as few files as possible.

With the new improvements, Angular 8.0 makes it possible to fully parallelized web worker bundling. It acts as a relief for front-end developers who face limitations in single-thread.

You can run the following if you want to create a new CLI with a web worker.

Ng Generate Web Worker My-Worker

On receiving the web worker, adopt the process to implement the web worker in your application. It is then bundled and code-split with the following line:

const worker = new Worker(`./my-worker.worker`, { type: `module` });

Angular Router Backwards Compatibility

Angular 8 comes with a backward compatibility mode which makes it simpler to upgrade the larger applications. The teams move to Angular with lazy loading of the AngularJS-based app’s parts, using the $route APIs.

FORMS

MarkAllAsTouched

MarkAllAsTouched is a method in Angular 8 used in the AbstractControl class.

It adds to the list of previous methods, such as  markAsDirty,  markAsTouched, and  markAsPending. The method gets available in all reactive form entities, since AbstractControl is the parent class of FormArrayFormGroup, and FormControl.

The method touches marks control and descendants as touched as shown below:

form.markAllAsTouched();

FormArray.clear

In Angular 8, the FormArray class offers a clear method, which quickly removes all control it contains. You no longer have to loop over every control one-by-one, as shown below:

// `users` is initialized with 2 users
const users = fb.array([user1, user2]);
users.clear();
// users is now empty
**ROUTER **

Location

To migrate to Angular 8, you should be aware of a few things added to the location services:

  • Offer access to the hostname, port, and protocol with platformLocation.
  • Get history.state with the new getState() method.
  • Ease testing with a MockPlatformLocation.

Lazy-Loading With Import Syntax

Lazy loading is a useful concept in Angular 8. It brings down the size of occasionally used files. It uses the standard dynamic import syntax from TypeScript, instead of the custom-string for lazy-loaded modules. The syntax has similarities with ECMAScript standard and supported only by Ivy.

So, what earlier looked like:

{ path: '/student', loadChildren: './student/student.module#StudentModule' }  

Now, looks as:

{ path: '/student', loadChildren: () => import('./student/student.module').then(s => s.StudentModule) } 

Service Worker

A service worker in Angular is a script that runs in a web browser and manages cache for an application. It augments the traditional web deployment model and empowers the application to deliver a reliable user experience with performance on par with natively-installed code.

Registration Strategy

In previous versions, the Service worker was waiting for a stable application to register. This was used to avoid slowing the start of an application. The new option has an option that specifies when the registration will take place.

However, if starting a recurring asynchronous task in the previous versions would never be considered by Angular as stable, the service work would not have registered.

But, with Angular 8, this is possible with the option of registrationStrategy for handling the registration for a service worker with the following values:

  • The default value is registerWhenStable.
  • Register immediately with registerImmediately, with no need to wait for the app and registers, right away.
  • Delay in milliseconds in $TIMEOUT with registerDelay:$TIMEOUT.
  • A custom strategy defines a return value as Observable. The Service Worker registers with the first value of the Observable.

Here is code snippet for Service Worker registration after 4 seconds:

**Bypass a Service Worker **

You can use the ngsw-bypass header, for bypassing the service worker with a specified request, as shown in the below code:

this.http.get(‘api/users’, { headers: { ‘ngsw-bypass’: true } });

Multiple Apps on SubDomains

Earlier it was not possible to use multiple applications for using @angular/service-worker with different sub-paths for the same domain. It was because the cache of each service worker overwrote the cache of others. This error gets fixed in this version.

**T****ypeScript 3.4 **

It is now mandatory for Angular 8 to use TypeScript 3.4. The development team must upgrade the TypeScript. It helps when using readable and clean JavaScript code. TypeScript 3.4 introduces a new flag named incremental. It saves information from the project graph from the last compilation. While using Incremental, the information detects the least costly way to emit changes for your project and type-check.

**Workspace APIs and Builder APIs in CLI **

Angular 8 has new builder APIs to tap ng build, ng test, and ng run. It has similarities with schematics that taps into ng addng generateng update, and  ng new.

It uses third-party tools and libraries to assist you in processes such as building and deployment. Moreover, Angular leverages Cloud for APIs. AngularFire is the official library that connects Angular with Firebase. AngularFire adds the deploy command to simplify the process of deployment and build by Firebase with the following:

ng add @angular/fire
ng run my-app:deploy

Moreover, in earlier version, for changes in workspace configuration, angular.json was modified manually using Schematics. But the new API, in the current version, makes it simpler to modify and read a file.

**AngularJS Migration **

Angular 8 enables the users of $location services, as it now allows a Location Upgrade Module in the AngularJS applications. It helps to translate the unified location service and shifts the responsibility from AngularJS $location to the Angular Location. It eases our applications with hybrid operations and depends on the upgrade along with route in AngularJS and part.

**Bazel **

The latest Angular version makes it easier to build CLI applications. All this is due to Bazel, developed by Google. It is a build tool that works well with any language inputs. Some benefits of Bazel are:

  • Build frontend and backend with the same tool.
  • Gain on rebuild time with tests and incremental builds.
  • Gain cache and remote builds on build farms.
  • Allow tasks with clear input or output with Bazel and ensure that all the necessary tasks run.

About IVY

Ivy is the prime feature of Angular 8 and included as an opt-in preview for testing. Ivy is a new compiler to build next-gen rendering pipelines in the current version. It increases the efficiency of the latest version. It helps to improve runtime speed with simplified incremental compiling with its ability to generate bundles of significantly smaller size.

Moreover, it uses the concept of incremental DOM, where each component gets compiled with a set of instructions that constitutes the DOM tree. It updates it with a change in data.

Developers can use Ivy to determine the potential and performance of Angular applications. It never alters any of the existing applications. On completion, it will make the Angular applications smaller, simpler, and faster.

Two Primary Concepts of Ivy

Local : Recompile only the changed components and allow quicker compiling.

**Treeshakable: **The unused code gets removed so that the application concentrates on the code used. Ivy acts beneficially when the UI of the application is clear to the developer.

Advantages of IVY

The advantages of Ivy are:

  • Smaller Bundles.
  • Reduced Payload Size.
  • Faster Rebuild times.
  • Enhance Backwards Compatibility .
  • Pre-compiled Code shipment .
  • Dismissal of metadata.json.
  • Rise of meta programming.
  • Improve template type checking.
  • Broad Compatibility with existing Angular applications.

**Notable Changes **

The angular team makes your life easier with Schematics. Schematic updates your code by simply running:

ng update @angular/cor

Query Timing

The decorators, ViewChild and ContentChild, now have a new option called static. If the queried element is static (not wrapped in ngIf and ngFor. Thenm, it is available in ngOnInit:

So,

<h1 #staticDiv>static</h1>

Gives

@ViewChild('staticDiv') staticDiv: ElementRef<HTMLDivElement>;

ngOnInit() {
  console.log('init static', this.staticDiv); // div
}

ngAfterViewInit() {
  console.log('after view init static', this.staticDiv); // div
}

A static flag, when introduced, not only breaks existing applications, but you can use the following to keep the old behavior, even when switching to Ivy. It ensures the same behavior as the current one.

@ViewChild('static', { static: true }) static: ElementRef<HTMLDivElement>;

You can check with the query migration guide for further information.

Template Variable ReAssignment

View Engine allowed the following:

<button
  *ngFor="let option of options"
  (click)="option = 'newButtonText'">{{ option }}</button>

However, this is no longer possible in Ivy. It does not allow us to assign a value to a template variable like an option in the above example.

When you upgrade to Angular 8 to prepare for the switch to Ivy, a schematic analyzes the template and issues a warning for such a case.

The option left is to fix it manually.

<button
  *ngFor="let option of options; index as index"
  (click)="options[index] = 'newButtonText'">{{ option }}</button>

**Document **

The token DOCUMENT gets moved from the @angular/platform-browser to  @angular/common. It is manually possible to change it with a schematic provided for the purpose.

**Remove Deprecated HTTP Package **

Angular 8 removed @angular/http replaced with @angular/common/http as in Angular 4.3. A schematic removes the dependency in package.json.

**Deprecated Web Worker Package **

The @angular/common/http package enables you to run an application in the Web Worker. It is included in the deprecated packages list and will be removed in the future.

You can find the list of all deprecated API packages obtained here. 

How to Update Angular 7 to Angular 8

Some of the things to consider are:

  • TypeScript 3.4 may cause some syntax error.
  • Run the $ node-vcommand to check the version of Node running in your system. You need to run Node 12 and beyond for the upgrade.
  • Run $ ng update @angular/material to run the Angular Material in the application.
**Conclusion **

Other than Ivy, the additions to Angular 8 are not that critical or significant. You can gather insights on the Ivy preview from the official Ivy guide provided. However, it is recommended to upgrade to Angular 8 to ensure that your apps are all ready for Ivy.

Moreover, Ivy will become the default in the next version of Angular, so now it the best option for you to check whether your apps are going to need any changes. Thank you !

Store user information with Angular 6, Nodejs and Express.

Store user information with Angular 6, Nodejs and Express.

Let’s create a very simple Angular form application that stores user information locally in memory on a server.

Let’s create a very simple Angular form application that stores user information locally in memory on a server.

On submitting the form, we’ll populate the data on the next page and add two additional elements to display the guid and customer uid.

The Portfolio API - Grow your coding career effortlessly | gitconnected

Getting Started

First we’ll need Node & NPM installed.

To check if you have node installed run this command in your terminal:

node -v

To confirm that you have npm installed you can run this command:

**npm -v**

If not, you can download & install NodeJS & NPM from https://nodejs.org/en/

Install the Angular CLI:

npm install -g @angular/cli

Generate a new Angular project:

ng new charts6

Navigate to http://localhost:4200/. At this point the application will automatically reload if you change any of the source files. The initial page should be the default Angular 6 page:

The main Application component will have a UI Router that initially shows the Input User Data Form component. On submitting the form**, Application** component will show the Display User Data component.

The Flow

Create components with ng cli

ng new angular-node-express
cd angular-node-express
ng generate component input-user-data-form --spec false

The CLI will create all the appropriate files and place them into the app folder

It should also add declarations into the app.module.ts

Next, we’ll add bootstrap 3 CSS library into the project to add styling.

npm install --save [email protected]

Afterwards add a path to “styles” array in the angular.json

"styles": [
              "node_modules/bootstrap/dist/css/bootstrap.min.css",
              "src/styles.css"
          ]

The Bootstrap grid system uses containers that hold rows and column. Rows and columns are percentage based. It is the container that changes responsively.

The container is used as a wrapper for the content.

We’ll choose a fluid container whose width will always be the width of the device.

fluid-container.html

<div class="container-fluid">

A grid row acts like a wrapper around the columns.

A row is created by adding the class row to an element inside the container.

fluid-container-row.html

<div class="container-fluid">
 <div class="row"></div>
</div>

Ideally, the number of columns equals 12 for every row. Different column class prefixes are used for different sized devices. Here 1 column takes up the size of 12 columns.

Change the app.component.html into the following:

app.component.html

<div class="container-fluid">
  <div class="row">
    <div class="col-xs-12">
      <h1>Hello World!</h1>
    </div>
  </div>
</div>
Running the application in DEV mode

Angular CLI uses webpack underneath to run the application on port 4200.

We’ll run our Node.js API endpoint on port 3000. But making an API call to another server (from port 4200) causes the CORS browser exception.

This is why we’ll use a proxy as a workround to the same-origin policy. The Proxy will re-route our API calls to a Node.js server to avoid the CORS exceptions. CORS is a browser security issue and does not apply to “backend to backend” communication.

All requests made to /api/... from within our application will be forwarded to [http://localhost:3000/api/...](http://localhost:3000/api/....)

With the proxy, our application diagram will look like this:

Create the proxy.conf.json in root folder

proxy.conf.json

{
  "/api/*": {
    "target": "http://localhost:3000",
    "secure": false,
    "logLevel": "debug",
    "changeOrigin": true
  }
}

When starting Angular & Proxy servers use the following command:

ng serve --proxy-config proxy.conf.json
Creating the Input User Data Component

First let's focus on creating UI and work on API endpoints thereafter

There are 3 rules you must follow when making a Bootstrap form:

  1. Every field must be wrapped in a div element
  2. Every should have .control-label class and its for value must match the corresponding <input>’s id, so when the label is clicked, the input becomes focused.
  3. Every should be given .form-control

Set the input-user-data-form.component.html to the following:

input-user-data-form.component.html

	<form>
	<div class="form-group"> <!-- First Name -->
		<label for="first_name" class="control-label">First Name</label>
		<input type="text" class="form-control" id="first_name" name="first_name" placeholder="Martin">
	</div>

	<div class="form-group"> <!-- Last Name -->
		<label for="last_name" class="control-label">Last Name</label>
		<input type="text" class="form-control" id="last_name" name="last_name" placeholder="Toha">
	</div>

	<div class="form-group"> <!-- Email -->
		<label for="email" class="control-label">E-mail</label>
		<input type="text" class="form-control" id="email" name="email" placeholder="[email protected]">
	</div>
							
	<div class="form-group"> <!-- Zip Code-->
		<label for="zipcode" class="control-label">Zip Code</label>
		<input type="text" class="form-control" id="zipcode" name="zipcode" placeholder="#####">
	</div>
	
	<div class="form-group"> <!-- Password -->
		<label for="password" class="control-label">Password</label>
		<input type="text" class="form-control" id="password" name="password" placeholder="*****">
	</div>
	
	<div class="form-group"> <!-- Register Button -->
		<button type="submit" class="btn btn-primary">Register</button>
	</div>

</form>

Change selector in the input-user-data-form.component.ts to input-user-data-form

And add it to app.component.html

Field Validation with Bootstrap

We are going to have to validate our fields prior to submitting the form to the backend. If a field is invalid, we can communicate it to the user by making the entire field red.

For each of the fields, we will:

Add the class .has-error to the .form-groupdiv

And create a <p>with the class .has-textto explain the problem

input-user-data-form.component-validation.html

<form>
	<div class="form-group has-error"> <!-- First Name -->
		<label for="first_name" class="control-label">First Name</label>
		<input type="text" class="form-control" id="first_name" name="first_name" placeholder="Martin">
		<p id="first_name_error" class="help-block">Invalid first name.</p>
	</div>

	<div class="form-group has-error"> <!-- Last Name -->
		<label for="last_name" class="control-label">Last Name</label>
		<input type="text" class="form-control" id="last_name" name="last_name" placeholder="Toha">
		<p id="last_name_error" class="help-block">Invalid last name.</p>
	</div>

	<div class="form-group has-error"> <!-- Email -->
		<label for="email" class="control-label">E-mail</label>
		<input type="text" class="form-control" id="email" name="email" placeholder="[email protected]">
		<p id="email_error" class="help-block">Invalid email address.</p>
	</div>
							
	<div class="form-group has-error"> <!-- Zip Code -->
		<label for="zipcode" class="control-label">Zip Code</label>
		<input type="text" class="form-control" id="zipcode" name="zipcode" placeholder="#####">
		<p id="zipcode_error" class="help-block">Invalid zip code.</p>
	</div>

	<div class="form-group has-error"> <!-- Password -->
		<label for="password" class="control-label">Password</label>
		<input type="text" class="form-control" id="password" name="password" placeholder="*****">
		<p id="password_error" class="help-block">Invalid password.</p>
	</div>
	
	<div class="form-group has-error"> <!-- Register Button -->
		<button type="submit" class="btn btn-primary">Register</button>
	</div>

</form>

Here is the form with errors added for every field:

Making a Reactive Form

A reactive form is a bit different from template-driven forms by providing more predictability with synchronous access to the data model, immutability with observable operators, and change tracking through observable streams.

To use the reactive form, we must import the FormsModule & ReactiveFormsModule from the @angular/forms package and add it to our app.module.ts

Next, lets modify the input-user-data-form.component.

We’ll inject the formBuilder into the component for use in the ngOnInit life cycle method. FormBuilder a service that helps making forms easier.

The FormBuilder will create a FormGroup userForm with FormControls. Each FormControl has a default value and a set of Validators.

Think of the userForm as a schema for the actual values. It holds validation rules for fields inside the form.

We’ll add registered & submitted boolean flags to the Component. We’ll use these to indicate the current form state**.**

Also, we’ll create a validator function for every input, see invalidFirstName(), invalidLastName(), invalidEmail(), invalidZipcode(), invalidPassword().

A validator function will be used in markup to hide / show error messages for each field.

input-user-data-form.components.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';

@Component({
  selector: 'input-user-data-form',
  templateUrl: './input-user-data-form.component.html',
  styleUrls: ['./input-user-data-form.component.css']
})

export class InputUserDataFormComponent implements OnInit {
	registered = false;
	submitted = false;
	userForm: FormGroup;

  constructor(private formBuilder: FormBuilder)
  {

  }

  invalidFirstName()
  {
  	return (this.submitted && this.userForm.controls.first_name.errors != null);
  }

  invalidLastName()
  {
  	return (this.submitted && this.userForm.controls.last_name.errors != null);
  }

  invalidEmail()
  {
  	return (this.submitted && this.userForm.controls.email.errors != null);
  }

  invalidZipcode()
  {
  	return (this.submitted && this.userForm.controls.zipcode.errors != null);
  }

  invalidPassword()
  {
  	return (this.submitted && this.userForm.controls.password.errors != null);
  }

  ngOnInit()
  {
  	this.userForm = this.formBuilder.group({
  		first_name: ['', Validators.required],
  		last_name: ['', Validators.required],
  		email: ['', [Validators.required, Validators.email]],
  		zipcode: ['', [Validators.required, Validators.pattern('^[0-9]{5}(?:-[0-9]{4})?$')]],
  		password: ['', [Validators.required, Validators.minLength(5), Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')]],
  	});
  }

  onSubmit()
  {
  	this.submitted = true;

  	if(this.userForm.invalid == true)
  	{
  		return;
  	}
  	else
  	{
  		this.registered = true;
  	}
  }

};

In the input-user-data-form.component.html, and for every Reactive form we’ll use a directive [formGroup] to bind to userForm variable we defined in the component

We’ll bind the onSubmit handler via the (ngSubmit) directive.

To connect an HTML input element to a Reactive form we need to add formControlNameattribute to each of the form inputs.

We’ll use the *ngIf & [ngClass] directives to control error messages. The styles and validation markup will get displayed after user attempts to submit the form. This is controlled with [submitted] property of the component.

After submitting the form, it will be validated every time user changes the input value, causing errors to show & hide dynamically.

input-user-data-form.component-02.html

<form [formGroup]="userForm" (ngSubmit)="onSubmit()">

	<div class="form-group" [ngClass]="{ 'has-error': invalidFirstName() }"> <!-- First Name -->
		<label for="first_name" class="control-label">First Name</label>
		<input type="text" formControlName="first_name" class="form-control" id="first_name" name="first_name" placeholder="Martin">
		<p *ngIf="invalidFirstName()" id="first_name_error" class="help-block">Invalid first name.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidLastName() }"> <!-- Last Name -->
		<label for="last_name" class="control-label">Last Name</label>
		<input type="text" formControlName="last_name" class="form-control" id="last_name" name="last_name" placeholder="Toha">
		<p *ngIf="invalidLastName()" id="last_name_error" class="help-block">Invalid last name.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidEmail() }"> <!-- Email -->
		<label for="email" class="control-label">E-mail</label>
		<input type="text" formControlName="email" class="form-control" id="email" name="email" placeholder="[email protected]">
		<p *ngIf="invalidEmail()" id="email_error" class="help-block">Invalid email address.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidZipcode() }"> <!-- Zip Code-->
		<label for="zipcode" class="control-label">Zip Code</label>
		<input type="text" formControlName="zipcode" class="form-control" id="zipcode" name="zipcode" placeholder="#####">
		<p *ngIf="invalidZipcode()" id="zipcode_error" class="help-block">Invalid zip code.</p>
	</div>

	<div class="form-group" [ngClass]="{ 'has-error': invalidPassword() }"> <!-- Password -->
		<label for="password" class="control-label">Password</label>
		<input type="password" formControlName="password" class="form-control" id="password" name="password" placeholder="*****">
		<p *ngIf="invalidPassword()" id="password_error" class="help-block">Invalid password.</p>
	</div>
	
	<div class="form-group has-error"> <!-- Register Button -->
		<button type="submit" class="btn btn-primary">Register</button>
	</div>

</form>

Here is what the form looks like initially:

And the form after filling out some fields with error:

Looks good so far! Lets continue with the next component.

Creating the Display User Data Component

The next Angular component will display user data. This will be a simple component that visualizes a data model passed into it.

Lets create a data model UserInfoModel in app/models, we’ll make it deserialize an object when its passed into the constructor.

UserInfoModel.ts

export class UserInfoModel
{
	guid: string;
	customerUid: string;
	
	first_name: string;
	last_name: string;

	email: string;
	zipcode: string;

	password: string;

	constructor(obj: any = null)
	{
		if(obj != null)
		{
			Object.assign(this, obj);
		}
	}
}

The view display-user-data.component.html binds to the model via interpolation {{…}} and displays the information.

display-user-data.component.html

<legend>User Information</legend>
<div>
	<div class="form-group row"> <!-- GUD -->
		<label class="col-xs-6 col-form-label">GUID</label>
		<div class="col-xs-6">{{user.guid}}</div>
	</div>

	<div class="form-group row"> <!-- Customer UID -->
		<label class="col-xs-6 col-form-label">Customer UID</label>
		<div class="col-xs-6">{{user.customerUid}}</div>
	</div>

	<div class="form-group row"> <!-- First Name -->
		<label class="col-xs-6 col-form-label">First Name</label>
		<div class="col-xs-6">{{user.first_name}}</div>
	</div>

	<div class="form-group row"> <!-- Last Name -->
		<label class="col-xs-6 col-form-label">Last Name</label>
		<div class="col-xs-6">{{user.last_name}}</div>
	</div>

	<div class="form-group row"> <!-- E-mail -->
		<label class="col-xs-6 col-form-label">E-mail</label>
		<div class="col-xs-6">{{user.email}}</div>
	</div>

	<div class="form-group row"> <!-- Zip Code -->
		<label class="col-xs-6 col-form-label">Zip Code</label>
		<div class="col-xs-6">{{user.zipcode}}</div>
	</div>

	<div class="form-group row"> <!-- Password -->
		<label class="col-xs-6 col-form-label">Password</label>
		<div class="col-xs-6">{{user.password}}</div>
	</div>

</div>

And just for testing purposes, DisplayUserDataComponent class will assign some default values into the UserInfoModel

display-user-data.component.ts

import { Component, OnInit } from '@angular/core';
import {UserInfoModel} from '../models/userInfo'

@Component({
  selector: 'display-user-data',
  templateUrl: './display-user-data.component.html',
  styleUrls: ['./display-user-data.component.css']
})

export class DisplayUserDataComponent implements OnInit {
	
	user: UserInfoModel = new UserInfoModel({guid: "D21ds12x", 
		customerUid: "cust2dsa12dsa", 
		first_name: "John", 
		last_name: "Doe", 
		email: "[email protected]", 
		zipcode: 10283,
		password: "Idasn2x2#"});

	constructor() { }

	ngOnInit()
	{

	}

}


Both of the pages look great!

Now, lets connect the Router into the AppComponent to establish a page flow

Connecting Routes & RouterModule

Our goal with routing is to have the InputUserDataFormComponentrendered when the url is /, and DisplayUserDataComponentshown when the url is /user/:uid

First, lets setup the imports to @angular/router, then define an array routes of our paths and destination pages.

const routes: Routes = [
  {
    path: '',
    component: InputUserDataFormComponent
  },
  {
    path: 'user/:uid',
    component: DisplayUserDataComponent
  }
];

In the importsadd RouterModule.forRoot(routes)

app.module-w-routes.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { ReactiveFormsModule, FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';
import { InputUserDataFormComponent } from './input-user-data-form/input-user-data-form.component';
import { DisplayUserDataComponent } from './display-user-data/display-user-data.component';
import { Routes, RouterModule } from "@angular/router";

const routes: Routes = [
  {
    path: '',
    component: InputUserDataFormComponent
  },
  {
    path: 'user/:uid',
    component: DisplayUserDataComponent
  }
];

@NgModule({
  declarations: [
    AppComponent,
    InputUserDataFormComponent,
    DisplayUserDataComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    ReactiveFormsModule,
    RouterModule.forRoot(routes)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Place the router-outletin the app.component.html

app.component-wroute.html

<div class="container-fluid">
  <div class="row">
    <div class="col-xs-12">
      <router-outlet></router-outlet>
    </div>
  </div>
</div>

Now the initial “/” root page looks like this:

Type “/user/a01” in the address bar and you get the User Information page

All works as intended on the client! Now lets continue with creation of API endpoint.

Creating API endpoint in Node.js & Express

We’ll use the express-generator-api to make a Node.js API quickly by generating a project.

Install express-generator:

npm install -g express-generator-api

Create the API:

express-api angular-node-express-api & cd angular-node-express-api

Install dependencies:

npm install

Тhe file structure generated should be the following:

At this point all the basics of an API have been implemented by the generator.

For example, body parser & cookie parser are part of the app.js. Here express will try identify any JSON data that comes into our application and parse it for us.

Next, we are going to make the following API endpoints.

/customer will insert a new customer and return auto-incremented customer.id

/customer/:uid will retrieve a single customer by customer uid

/generate_guid will generate a tracking_guide that Angular UI will use when creating a new customer

Router /generate_uid

Lets copy users.js to generate_uid.js just to have a basic router. We’ll modify this router to return a random uid

Install uid-safe, which generates a uid safe for cookies

npm install uid-safe

Include it in generate_uid.js

var uid = require('uid-safe')

And return it with the response

generate_uid.js

var express = require('express');
var uid = require('uid-safe');

var router = express.Router();

/* GET a guid. */
router.get('/', function(req, res, next)
{
	var strUid = uid.sync(18);

	res.json({guid: strUid});
});

module.exports = router;

We’ll need to hook up the generate_uid router in the app.js

app.use('/api/v1/generate_uid', generate_uid);

The route returns a JSON with the GUID. Great!

{"guid":"K7VPC3I9kxIJ4Ct2_2ZR7Xb1"}

Router /customer

For every API service I usually have a model for the data and a service that performs all CRUD operations with it; that way there is a level of abstraction between the API request, storage & validation, but also a good way to access the service from anywhere in the application.

In this example, we’ll simply store customer data in memory.

The service will increment a customer UID every time a new customer is created.

We’ll use that customer UID to retrieve the customer data as well.

Create the models directory and add the following model.customer.js in there.

model.customer.js

class CustomerModel
{
	constructor(uid, first_name, last_name, email, zipcode, password)
	{
		this.uid = uid;
		this.first_name = first_name;
		this.last_name = last_name;
		this.email = email;
		this.zipcode = zipcode;
		this.password = password;
	}
}

module.exports = CustomerModel;

Then, create services directory and add a service.customer.js in there.

CustomerService implements all the CRUD operations create, read, update and delete the CustomerModel. It uses the counter to increment the customer uid; and stores all the customers into the customers object.

It’s important to validate our data on the server as well as the client. I usually keep validation as part of the service object, which is either a separate method or part of the CRUD operation.

We’ll implement the validation using the fastest-validator library.

Install fastest-validator library:

npm install fastest-validator --save

Import it in the CustomerService service object and create an instance of the Validator with custom schema.

var validator = require('fastest-validator');

The Schema object contains all validation rules, which is then used by the Validator instance to validate data against it.

Then, we’ll need to notify UI if validation fails on the server. If that happens, the create operation will throw an Error with list of errors for every failed validation.


services\service.customer.js

The list of errors will propagate into the response, which will be handled by Angular to communicate an error.

service.customer.js

const CustomerModel = require("../models/model.customer");
let Validator = require('fastest-validator');


let customers = {};
let counter = 0;

/* create an instance of the validator */
let customerValidator = new Validator();

/* use the same patterns as on the client to validate the request */
let namePattern = /([A-Za-z\-\’])*/;
let zipCodePattern = /^[0-9]{5}(?:-[0-9]{4})?$/;
let passwordPattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$/;

/* customer validator shema */
const customerVSchema = {
		guid: {type: "string", min: 3},
		
		first_name: { type: "string", min: 1, max: 50, pattern: namePattern},
		last_name: { type: "string", min: 1, max: 50, pattern: namePattern},
		email: { type: "email", max: 75 },
		zipcode: { type: "string", max: 5, pattern: zipCodePattern},

		password: { type: "string", min: 2, max: 50, pattern: passwordPattern}
	};

/* static customer service class */
class CustomerService
{
	static create(data)
	{
		var vres = customerValidator.validate(data, customerVSchema);
		
		/* validation failed */
		if(!(vres === true))
		{
			let errors = {}, item;

			for(const index in vres)
			{
				item = vres[index];

				errors[item.field] = item.message;
			}
			
			throw {
			    name: "ValidationError",
			    message: errors
			};
		}

		let customer = new CustomerModel(data.first_name, data.last_name, data.email, data.zipcode, data.password);

		customer.uid = 'c' + counter++;

		customers[customer.uid] = customer;

		return customer;
	}

	static retrieve(uid)
	{
		if(customers[uid] != null)
		{
			return customers[uid];
		}
		else
		{
			throw new Error('Unable to retrieve a customer by (uid:'+ uid +')');
		}
	}

	static update(uid, data)
	{
		if(customers[uid] != null)
		{
			const customer = customers[uid];
			
			Object.assign(customer, data);
		}
		else
		{
			throw new Error('Unable to retrieve a customer by (uid:'+ cuid +')');
		}
	}

	static delete(uid)
	{
		if(customers[uid] != null)
		{
			delete customers[uid];
		}
		else
		{
			throw new Error('Unable to retrieve a customer by (uid:'+ cuid +')');
		}
	}
}

module.exports = CustomerService;

Lets connect the CustomerService to express router.

We’ll be using the async / await, which is a better way to write asynchronous code. Previous options are callbacks and promises, in fact async/await is built on top of promises; but async / await make the code behave & read a little more like synchronous code.

In the following router we implement all the CRUD operations using the CustomerService singleton.

Take a look at the create operation, where we submit user data and how errors are propagated into the response via try / catch block. The code is easy to read.


routes-customer.js

var express = require('express');
var router = express.Router();
var CustomerService = require('../services/service.customer');

/* GET customer listing. */
router.get('/', async function(req, res, next)
{
	res.json({error: "Invalid Customer UID."});
});

/* adds a new customer to the list */
router.post('/', async (req, res, next) =>
{
	const body = req.body;

	try
	{
		const customer = await CustomerService.create(body);

		if(body.guid != null)
		{
			customer.guid = body.guid;
		}

		res.cookie('guid', customer.guid, { maxAge: 900000, httpOnly: true });

		// created the customer! 
		return res.status(201).json({ customer: customer });
	}
	catch(err)
	{
		if (err.name === 'ValidationError')
		{
        	return res.status(400).json({ error: err.message });
		}

		// unexpected error
		return next(err);
	}
});

/* retrieves a customer by uid */
router.get('/:id', async (req, res, next) =>
{
	try
	{
		const customer = await CustomerService.retrieve(req.params.id);

		return res.json({ customer: customer });
	}
	catch(err)
	{
		// unexpected error
		return next(err);
	}
});

/* updates the customer by uid */
router.put('/:id', async (req, res, next) =>
{
	try
	{
		const customer = await CustomerService.update(req.params.id, req.body);

		return res.json({ customer: customer });
	}
	catch(err)
	{
		// unexpected error
		return next(err);
	}
});

/* removes the customer from the customer list by uid */
router.delete('/:id', async (req, res, next) =>
{
	try
	{
		const customer = await CustomerService.delete(req.params.id);

		return res.json({success: true});
	}
	catch(err)
	{
		// unexpected error
		return next(err);
	}
});

module.exports = router;

Next, we’ll need to modify the input-user-data-form.component & its template to support showing the server side errors, in case the validation fails on the server.

In the InputUserDataFormComponent we’ll add the serviceErrors object, which will contain all the server side errors for each fields. And add to the validator methods to check if the error message for that field exists, like so:


app\input-user-data-form.component.ts

We’ll also modify the onSubmit method, and add an error handler to our request which will simply set serviceErrors object.


app\input-user-data-form\input-user-data-form.component.ts

Next we’ll add a reference to server side errors in the input-user-data-form.component.html template to display a server side error in case it exists:


app\input-user-data-form\input-user-data-form.component.html

Now, we’ll know if validation fails on the server, since messages are going to be propagated to Angular UI.

Everything looks good, lets connect our routers in the app.js


app-routes.js

var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var generate_uid = require('./routes/generate_uid');
var customer = require('./routes/customer');


var app = express();

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser())

app.use('/api/v1/customer', customer);
app.use('/api/v1/generate_uid', generate_uid);

module.exports = app;

We are almost there!

We are done creating the application, now a couple of minor details.

We are using a proxy in order to go around the CORS browser issue by having the Webpack on port 4200 communicate with Node.js server on port 3000. This is a good solution for the development environment, but we’ll need to have a better implementation when moving to production.

The API server will need to either enable the API for all origins or white list our access point.

For now, lets just enable CORS for all origins

To enable our API for all is easy, just add the following code to app.js

app-cors-enabled.js

app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});

Uncaught Exceptions

Another minor detail we haven’t covered is handling of Uncaught Exceptions

Because Node.js runs on a single processor uncaught exceptions are an issue to be aware of when developing applications.

When your application throws an uncaught exception you should consider that your application is now running in an unclean state. You can’t reliably continue your program at this point because you don’t really know what got affected by the error.

The best way to handle a crash is to collect as much data about it, send the errors to an external crash service or log it; and restart the server.

For our simple application, we’ll implement the appropriate functions, just place these in the beginning of your app.js

I’ll keep the reporter method empty, which we will connect in a later article to an external crash service

app-uncaught.js

let reporter = function (type, ...rest)
{
	// remote reporter logic goes here
};

/* handle an uncaught exception & exit the process */
process.on('uncaughtException', function (err)
{
	console.error((new Date).toUTCString() + ' uncaughtException:', err.message);
	console.error(err.stack);

	reporter("uncaughtException", (new Date).toUTCString(), err.message, err.stack);

	process.exit(1);
});

/* handle an unhandled promise rejection */
process.on('unhandledRejection', function (reason, promise)
{
	console.error('unhandled rejection:', reason.message || reason);

	reporter("uncaughtException", (new Date).toUTCString(), reason.message || reason);
})

Finally! To start the API server:

npm start

Try hitting the http://localhost:3000/api/v1/generate_uid

or http://localhost:3000/api/v1/customer

And you should see some JSON data.

Integrating services into the Angular application

To quickly integrate the Angular application we are going to:

Import & Inject HttpClient & Router into the InputUserDataFormComponent

Next, we’ll retrieve the GUID from the ‘/api/v1/generate_uid’ API service in the constructor of the InputUserDataFormComponent and set the guid variable


constructor of InputUserDataFormComponent

In the onSubmit method of the InputUserDataFormComponent we’ll post the data into the ‘/api/v1/customer’ service & navigate the Angular Router to the next page /user/:id

The full source code:

input-user-data-form.component-wroute-api.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { HttpClient } from "@angular/common/http";
import { Router } from "@angular/router";

@Component({
  selector: 'input-user-data-form',
  templateUrl: './input-user-data-form.component.html',
  styleUrls: ['./input-user-data-form.component.css']
})

export class InputUserDataFormComponent implements OnInit {
	registered = false;
	submitted = false;
	userForm: FormGroup;
	guid: string;
	serviceErrors:any = {};

  constructor(private formBuilder: FormBuilder, private http: HttpClient, private router: Router)
  {
  	this.http.get('/api/v1/generate_uid').subscribe((data:any) => {
      this.guid = data.guid;
    }, error => {
        console.log("There was an error generating the proper GUID on the server", error);
    });
  }

  invalidFirstName()
  {
  	return (this.submitted && (this.serviceErrors.first_name != null || this.userForm.controls.first_name.errors != null));
  }

  invalidLastName()
  {
  	return (this.submitted && (this.serviceErrors.last_name != null || this.userForm.controls.last_name.errors != null));
  }

  invalidEmail()
  {
  	return (this.submitted && (this.serviceErrors.email != null || this.userForm.controls.email.errors != null));
  }

  invalidZipcode()
  {
  	return (this.submitted && (this.serviceErrors.zipcode != null || this.userForm.controls.zipcode.errors != null));
  }

  invalidPassword()
  {
  	return (this.submitted && (this.serviceErrors.password != null || this.userForm.controls.password.errors != null));
  }

  ngOnInit()
  {
  	this.userForm = this.formBuilder.group({
  		first_name: ['', [Validators.required, Validators.maxLength(50)]],
  		last_name: ['', [Validators.required, Validators.maxLength(50)]],
  		email: ['', [Validators.required, Validators.email, Validators.maxLength(75)]],
  		zipcode: ['', [Validators.required, Validators.pattern('^[0-9]{5}(?:-[0-9]{4})?$')]],
  		password: ['', [Validators.required, Validators.minLength(5), Validators.pattern('^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])[a-zA-Z0-9]+$')]],
  	});
  }

  onSubmit()
  {
  	this.submitted = true;

  	if(this.userForm.invalid == true)
  	{
  		return;
  	}
  	else
  	{
  		let data: any = Object.assign({guid: this.guid}, this.userForm.value);

  		this.http.post('/api/v1/customer', data).subscribe((data:any) => {
	      
	      let path = '/user/' + data.customer.uid;

	      this.router.navigate([path]);
	    }, error =>
	    {
	    	this.serviceErrors = error.error.error;
        });

  		this.registered = true;

  	}
  }

};

Then lets Import & Inject HttpClient & ActivatedRoute into the DisplayUserDataComponent

Retrieve customer data from the ‘/api/v1/customer/:id’ API and populate the UserInfoModel with new data, and have DisplayUserDataComponent view redraw itself with new data via interpolation {{…}}

display-user-data.component-wroute-api.ts

import { Component, OnInit } from '@angular/core';
import {UserInfoModel} from '../models/userInfo';
import { HttpClient } from "@angular/common/http";
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'display-user-data',
  templateUrl: './display-user-data.component.html',
  styleUrls: ['./display-user-data.component.css']
})

export class DisplayUserDataComponent implements OnInit
{

	user: UserInfoModel = new UserInfoModel({guid: "D21ds12x", 
		uid: "cust2dsa12dsa", 
		first_name: "John", 
		last_name: "Doe", 
		email: "[email protected]", 
		zipcode: 10283,
		password: "Idasn2x2#"});

	constructor(private http: HttpClient, private route: ActivatedRoute) {

	}

	private subscriber: any;

	ngOnInit()
	{
		this.subscriber = this.route.params.subscribe(params => {
	       
	       this.http.get('/api/v1/customer/' + params.uid).subscribe((data:any) => {

				this.user = new UserInfoModel(data.customer);
		    });
	    });
	}

}

The initial page of the application ‘/’:

Fill in the form and press Register:

Next page, the user information is displayed.

After creating a customer, you can also type in the customer url followed by customer uid to retrieve customer data:

http://localhost:3000/api/v1/customer/c0

{"customer":{"first_name":"John","last_name":"Doe","email":"[email protected]","zipcode":"12345","password":"Udsakln12dsa","uid":"c0","guid":"gCnqJdp3saMNPpJfXPj6DORy"}}

Source Code is accessible at Github bellow:

Angular UI:
https://github.com/jsmuster/angular-node-express

Node.js + Express API:
https://github.com/jsmuster/angular-node-express-api