Which Query Builder/ORM should you use for Nodejs

Which Query Builder/ORM should you use for Nodejs

<strong>What is an ORM ? ORM solutions are useful to facilitate data-driven API development. Users have concrete needs which drive the data model of an application. In legacy development, this data architecture is typically implemented and version controlled using database scripts such as SQL scripts. A separate library is then used for the server application to execute CRUD actions on the database.</strong>

What is an ORM ? ORM solutions are useful to facilitate data-driven API development. Users have concrete needs which drive the data model of an application. In legacy development, this data architecture is typically implemented and version controlled using database scripts such as SQL scripts. A separate library is then used for the server application to execute CRUD actions on the database.

Why is it so Important?

ORMs work as a high-level API to execute CRUD, and these days quality ORMs also allow us to initialize the data through code. Complex data manipulation, cleaning and so on, is often easier in code. While dedicated Extract, Transform and Load (ETL) tools exist, the same ETL tasks can be easily implemented in ORM.

Implementing extract, transform, and load with code allows a system to more easily integrate data from very different sources. SQL databases of multiple flavors, NoSQL data, file system data, and third party data can all be integrated under a single language with a JavaScript ORM.

Finally, code-oriented data control also allows a system to implement data usage at run time or in the build process, and flexibly adapt usage during the development process as needed.

To restate, ORMs help to abstract data mappings between your code and the database, easing data querying and manipulation. It can also help to easily change the underlying database engine without (mostly) changing any code. The following are a few ORMs you can use with nodejs.


Should I use ORM like sequelize for PostgreSQL/Node.js?

To begin with it depends on the scale of your project and team working on that project. If you have a small scaled project which may need less customizations later on you can simply go for query based execution. But if you have a large project with customizations coming at a later stage try to develop yourself or your team to use an ORM like sequelize.

So the overall process needed to move to ORM is:

1. You should have great understanding of the queries and relations of the database schema.

2. You should understand the language your ORM supports. So for Node.js one should have a skilled understanding of basic Javascript, Callbacks and Promises.

3. Now if you know the queries well and can work with ORM too. You will speed your development process manyfolds.

Pros:

  • Standardization – ORMs usually have a single schema definition in the code. This makes it very clear what the schema is, and very simple to change it.
  • No need to learn SQL – queries are written in plain JavaScript.
  • Portable – with an ORM, it is easy to migrate between databases (we actually did that already, moving some older PostgreSQL databases to Amazon Aurora with the MySQL standardization). Because all your code uses the ORM, you only need to change / reimplement the ORM to replace the DB. Many ORMs (like Sequalize) support multiple popular databases out of the box.

Cons:

  • **It is hard to implement complex queries **– while ORMs simplify querying – especially if you lack experience with SQL – they can prove more difficult and non-flexible when writing complex queries (with aggregations, sub-queries, joins, etc…). (I know it’s supported, but it often feels very roundabout compared to SQL).
  • Can be inefficient – due to the previous point, ORMs can lead to inefficient querying (you’re writing a query that fits the ORM, not a simple / efficient query). Because you’re not using SQL, the query execution also becomes obscure (can’t run SQL explain and immediately understand what the query plan is).
  • New language – if you’re familiar with SQL, an ORM can feel like re-learning a new query language.

_Bottom line _– for smaller projects / projects that involve very complex queries, I’d recommend sticking with SQL. For bigger projects with bigger teams touching the codebase and making schema changes, a good ORM can be a life saver.

Following are some popular ORM :

1. Sequelize

Sequelize is another ORM for Node.js and io.js (which are finally merging together). It supports PostgreSQL, MySQL, MariaDB, SQLite and MSSQL and features solid transaction support, relations, read replication, and

more. You can install it by running the following commands:

# Install Sequelize
$ npm install --save sequelize
Install the DB driver

$ npm install --save pg pg-hstore

For both mysql and mariadb dialects

$ npm install --save mysql
$ npm install --save sqlite3

MSSQL

$ npm install --save tedious

Now you are ready to use it as shown in the example below:

var Sequelize = require('sequelize');
var sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'|'mariadb'|'sqlite'|'postgres'|'mssql', // use one of these

pool: {
max: 5,
min: 0,
idle: 10000
},

// SQLite only
storage: 'path/to/database.sqlite'
});

// Or you can simply use a connection uri
var sequelize = new Sequelize('postgres://user:[email protected]:5432/dbname');

Just like with Bookshelf.js, you need only one connection to the database. After that, you can create a model like:

var User = sequelize.define('user', {
firstName: {
type: Sequelize.STRING,
field: 'first_name' // first_name column matches User.firstName
},
lastName: {
type: Sequelize.STRING
}
}, {
freezeTableName: true // Model tableName (user) will be the same as the model name
});

The above Sequelize.STRING matches a VARCHAR in SQL. Other data types are Sequelize.INTEGER for INTEGERSequelize.BLOB for BLOB (or bytea in Postgres). You can read the full list here.

Sequelize allows you to write relations between the tables. For example, if you have a model called Project and another one called Developer and want to assign more than one developer to one project, you can do it like this:

Project.hasMany(Developer, {as: 'devs'})

This will make sure to add the necessary fields in each model (project_id to the Developer model in this case). Or if you feel you can’t profit from the Sequelize API, you can run raw SQL queries.

Sequelize can be found on GitHub as well

2. Bookshelf

Bookshelf is a JavaScript ORM for Node.js, built on the Knex SQL query builder. Featuring both promise based and traditional callback interfaces, providing transaction support, eager/nested-eager relation loading, polymorphic associations, and support for one-to-one, one-to-many, and many-to-many relations. It is designed to work well with PostgreSQL, MySQL, and SQLite3.

While Bookshelf primarily targets Node.js, all dependencies are browser compatible, and it could be adapted to work with other javascript environments supporting a sqlite3 database, by providing a custom Knex adapter.

|

var knex = require('knex')({
client: 'mysql',
connection: {
host : '127.0.0.1',
user : 'your_database_user',
password : 'your_database_password',
database : 'myapp_test',
charset : 'utf8'
}
});

var bookshelf = require('bookshelf')(knex);

var User = bookshelf.Model.extend({
tableName: 'users'
});

|

3. Objection.js

Objection.js is an ORM for Node.js that aims to stay out of your way and make it as easy as possible to use the full power of SQL and the underlying database engine.

Objection.js, like Bookshelf, is built on the wonderful SQL query builder knex. All databases supported by knex are supported by objection.js. SQLite3, Postgres and MySQL are thoroughly tested.

4. Lovefiled

Lovefield is not a real ORM. It’s actually a relational database for web apps, built upon IndexedDB, developed by Google and written entirely in JavaScript. It doesn’t support raw SQL queries, but it comes with an API that tries to mimic the SQL syntax.

Besides downloading directly from GitHub repository, Lovefield supports npm and bower package management systems and can be found using

npm info lovefield
bower info lovefield

Adding Lovefield as the dependency and executing npm update or bower update will automatically pull down the designated release.

Defining Schema

The concept of Lovefield is to define a database schema, then operate on the instance implementing that schema. In the example, schema definition is carried out through a set of synchronous APIs:

// SQL equivalent: CREATE DATABASE IF NOT EXISTS todo
// This schema definition (or data definition commands in SQL, DDL) is not
// executed immediately. Lovefield uses builder pattern to build the schema
// first, then performs necessary database open/creation later.
var schemaBuilder = lf.schema.create('todo', 1);

// SQL equivalent:
// CREATE TABLE IF NOT EXISTS Item (
// id AS INTEGER,
// description AS INTEGER,
// deadline as DATE_TIME,
// done as BOOLEAN,
// PRIMARY KEY ON ('id')
// );
// ALTER TABLE Item ADD INDEX idxDeadLine(Item.deadline DESC);
schemaBuilder.createTable('Item').
addColumn('id', lf.Type.INTEGER).
addColumn('description', lf.Type.STRING).
addColumn('deadline', lf.Type.DATE_TIME).
addColumn('done', lf.Type.BOOLEAN).
addPrimaryKey(['id']).
addIndex('idxDeadline', ['deadline'], false, lf.Order.DESC);

The code above has pseudo SQL commands to demonstrate their equivalent concept in SQL. Once the schema is defined, Lovefield needs to be instructed to create or connect to the corresponding instance:

// Promise-based API to get the instance.
schemaBuilder.connect().then(function(db) {
// ...
});

From this point on, the schema cannot be altered. Both the connect() and Lovefield offered query APIs are asynchronous Promise-based APIs. This design is to prevent Lovefield from blocking main thread since the queries can be long running and demanding quite some CPU and I/O cycles.

If the database is brand new, Lovefield will create it using the schema. If the database already exists, Lovefield will attempt to identify the instance using database name specified in the schema, and connect to it.

Lovefield also uses Promise chaining pattern extensively:

// Start of the Promise chaining
schemaBuilder.connect().then(function(db) {
// Asynchronous call connect() returned object: db
todoDb = db;

// Get the schema representation of table Item.
// All schema-related APIs are synchronous.
item = db.getSchema().table('Item');

// Creates a row. Lovefield does not accept plain objects as row.
// Use the createRow() API provided in table schema to create a row.
var row = item.createRow({
'id': 1,
'description': 'Get a cup of coffee',
'deadline': new Date(),
'done': false
});

// INSERT OR REPLACE INTO Item VALUES row;
// The exec() method returns a Promise.
return db.insertOrReplace().into(item).values([row]).exec();

}).then(function() {
// When reached here, Lovefield guarantees previous INSERT OR REPLACE
// has been committed with its implicit transaction.

// SELECT * FROM Item WHERE Item.done = false;
// Return another Promise by calling this SELECT query's exec() method.
return todoDb.select().from(item).where(item.done.eq(false)).exec();

}).then(function(results) {
// The SELECT query's Promise will return array of rows selected.
// If there were no rows, the array will be empty.

results.forEach(function(row) {
// Use column name to directly dereference the columns from a row.
console.log(row['description'], 'before', row['deadline']);
});
});

5. waterline

Waterline is a new kind of storage and retrieval engine.

It provides a uniform API for accessing stuff from different kinds of databases, protocols, and 3rd party APIs. That means you write the same code to get and store things like users, whether they live in Redis, mySQL, LDAP, MongoDB, or Postgres.

Waterline strives to inherit the best parts of ORMs like ActiveRecord, Hibernate, and Mongoose, but with a fresh perspective and emphasis on modularity, testability, and consistency across adapters.

Originally published at socialdribbler.com

Learn more

☞ MERN Stack Front To Back: Full Stack React, Redux & Node.js

☞ The Complete Node.js Developer Course (3rd Edition)

☞ Learn and Understand NodeJS

☞ NodeJS - The Complete Guide (incl. MVC, REST APIs, GraphQL)

☞ React Node FullStack - Social Network from Scratch to Deploy

☞ Node.js Absolute Beginners Guide - Learn Node From Scratch

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.

Angular 7 CRUD with Nodejs and MySQL Example

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

Angular CRUD with Nodejs and MySQL Example

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

Docker Best Practices for Node Developers

Docker Best Practices for Node Developers

Welcome to the "Docker Best Practices for Node Developers"! With your basic knowledge of Docker and Node.js in hand, Docker Mastery for Node.js is a course for anyone on the Node.js path. This course will help you master them together.

Welcome to the best course on the planet for using Docker with Node.js! With your basic knowledge of Docker and Node.js in hand, Docker Mastery for Node.js is a course for anyone on the Node.js path. This course will help you master them together.

My talk on all the best of Docker for Node.js developers and DevOps dealing with Node apps. From DockerCon 2019. Get the full 9-hour training course with my coupon at http://bit.ly/365ogba

Get the source code for this talk at https://github.com/BretFisher/dockercon19

Some of the many cool things you'll do in this course
  • Build Node.js Images that auto-scan for security vulnerabilities
  • Use Docker's cutting-edge BuildKit with SSH Agents and NPM Caches for better image building
  • Use docker-compose with Visual Studio Code for full Node.js debug support
  • Use BuildKit and Multi-stage Builds to create minimal and flexible Dockerfiles
  • Build custom Node.js images using distro's like CentOS and Alpine
  • Test Docker init, tini, and Node.js as a PID 1 process in containers
  • Create Node.js apps that properly startup and respond to healthchecks
  • Develop ARM based Node.js apps with Docker Desktop, and deploy to AWS A1 Servers
  • Build graceful shutdown code into your apps for zero-downtime deploys
  • Dig into HTTP connections with orchestration, and how Proxies can help
  • Study examples of Docker Swarm and Kubernetes deployments for Node.js
  • Spend time Migrating traditional (legacy) Node.js apps into containers
  • Simplify your microservice solutions with advanced Docker Compose features
What you will learn in this course

You'll start with a quick review about getting set up with Docker, as well as Docker Compose basics. That way we're on the same page for the basics.

Then you'll jump into Node.js Dockerfile basics, that way you'll have a good Dockerfile foundation for new features we'll add throughout the course.

You'll be building on all the different things you learn from each Lecture in the course. Once you have the basics down of Compose, Dockerfile, and Docker Image, then you'll focus on nuances like how Docker and Linux control the Node process and how Docker changes that to make sure you know what options there are for starting up and shutting down Node.js and the right way to do it in different scenarios.

We'll cover advanced, newer features around making the Dockerfile the most efficient and flexible as possible using things like BuildKit and Multi-stage.

Then we'll talk about distributed computing and cloud design to ensure your Node.js apps have 12-factor design in your containers, as well as learning how to migrate old apps into this new way of doing things.

Next we cover Compose and its awesome features to get really efficient local development and test set-up using the Docker Compose command line and Docker Compose YAML file.

With all this knowledge, you'll progress to production concerns and making images production-ready.

Then we'll jump into deploying those containers and running them in production. Whether you use Docker Engine or orchestration with Kubernetes or Swarm, I've got you covered. In addition, we'll cover HTTP connections and reverse proxies for connection handling and routing with multi-container systems.

Lastly, you'll get a final, big assignment where you'll be building and deploying a large, complex solution, including multiple Node.js containers that are doing different things. You'll build Docker images, Dockerfiles, and compose files, and deploy them to a server to test. You'll need to check whether connections failover properly. You'll basically take everything you've learned and apply it in one big project!

What's new capabilities in Node.js 13?

What's new capabilities in Node.js 13?

Node.js 13 brings programming enhancements, worker threads. Node.js 13, the latest version of the popular JavaScript runtime, was released this week, emphasizing worker threads, programming enhancements, and internationalization capabilities.

Node.js 13, the latest version of the popular JavaScript runtime, was released this week, emphasizing worker threads, programming enhancements, and internationalization capabilities.

Node.js 13 replaces Node.js 12 as the “current” release but Node.js 12 remains the long-term support (LTS) release. Thus Node.js 13 is not recommended for production use. Nevertheless, Node.js 13 will be useful for building and testing the latest features. Developers can use Node.js 13 to ensure that their packages and applications will be compatible with future versions.

These are the key new capabilities in Node.js 13:

  • Worker threads for performing CPU-intensive JavaScript operations are now stable in both Node.js 12 and Node.js 13.
  • Node.js releases now are built with default full-ICU (International Components for Unicode) support. All locales supported by ICU are included and Intl-related APIs may return different values than before.
  • N-API, for building native add-ons, has been updated with additional supported functions.
  • If the validation function passed to assert.throws() or assert.rejects() returns a value besides true, an assertion error will be thrown instead of the original error. This will highlight the programming mistake. Also, if a constructor function is passed to validate the instance of errors thrown in assert.throw() or assert.reject(), an assertion will be thrown instead of the original error.
  • The minimum supported version of Xcode is now Xcode 10. Xcode is Apple’s integrated development environment, available only for MacOS. Developers can continue to use Xcode 8 for now, but this may change in a future Node.js 13.x release.
  • The Google V8 JavaScript engine used in Node.js has been updated to version 7.8, which brings performance improvements for object destructuring, memory usage, and WebAssembly startup time.
  • For HTTP communications, data will no longer be emitted after a socket error. In addition, the legacy HTTP parser has been removed and the request.connection and response.connection properties have been runtime deprecated. The equivalent request.socket and response.socket should be used instead.
  • The timing and behavior of streams was consolidated for several edge cases.
Where to download Node.js

You can download Node.js from the project website.

Machine Learning In Node.js With TensorFlow.js

Machine Learning In Node.js With TensorFlow.js

Machine Learning In Node.js With TensorFlow.js - TensorFlow.js is a new version of the popular open-source library which brings deep learning to JavaScript. Developers can now define, train, and run machine learning models using the high-level library API.

Machine Learning In Node.js With TensorFlow.js - TensorFlow.js is a new version of the popular open-source library which brings deep learning to JavaScript. Developers can now define, train, and run machine learning models using the high-level library API.

Pre-trained models mean developers can now easily perform complex tasks like visual recognitiongenerating music or detecting human poses with just a few lines of JavaScript.

Having started as a front-end library for web browsers, recent updates added experimental support for Node.js. This allows TensorFlow.js to be used in backend JavaScript applications without having to use Python.

Reading about the library, I wanted to test it out with a simple task... 🧐

Use TensorFlow.js to perform visual recognition on images using JavaScript from Node.js
Unfortunately, most of the documentation and example code provided uses the library in a browser. Project utilities provided to simplify loading and using pre-trained models have not yet been extended with Node.js support. Getting this working did end up with me spending a lot of time reading the Typescript source files for the library. 👎

However, after a few days' hacking, I managed to get this completed! Hurrah! 🤩

Before we dive into the code, let's start with an overview of the different TensorFlow libraries.

TensorFlow

TensorFlow is an open-source software library for machine learning applications. TensorFlow can be used to implement neural networks and other deep learning algorithms.

Released by Google in November 2015, TensorFlow was originally a Python library. It used either CPU or GPU-based computation for training and evaluating machine learning models. The library was initially designed to run on high-performance servers with expensive GPUs.

Recent updates have extended the software to run in resource-constrained environments like mobile devices and web browsers.

TensorFlow Lite

Tensorflow Lite, a lightweight version of the library for mobile and embedded devices, was released in May 2017. This was accompanied by a new series of pre-trained deep learning models for vision recognition tasks, called MobileNet. MobileNet models were designed to work efficiently in resource-constrained environments like mobile devices.

TensorFlow.js

Following Tensorflow Lite, TensorFlow.js was announced in March 2018. This version of the library was designed to run in the browser, building on an earlier project called deeplearn.js. WebGL provides GPU access to the library. Developers use a JavaScript API to train, load and run models.

TensorFlow.js was recently extended to run on Node.js, using an extension library called tfjs-node.

The Node.js extension is an alpha release and still under active development.

Importing Existing Models Into TensorFlow.js

Existing TensorFlow and Keras models can be executed using the TensorFlow.js library. Models need converting to a new format using this tool before execution. Pre-trained and converted models for image classification, pose detection and k-nearest neighbours are available on Github.

Using TensorFlow.js in Node.js

Installing TensorFlow Libraries

TensorFlow.js can be installed from the NPM registry.

npm install @tensorflow/tfjs @tensorflow/tfjs-node
// or...
npm install @tensorflow/tfjs @tensorflow/tfjs-node-gpu

Both Node.js extensions use native dependencies which will be compiled on demand.

Loading TensorFlow Libraries

TensorFlow's JavaScript API is exposed from the core library. Extension modules to enable Node.js support do not expose additional APIs.

const tf = require('@tensorflow/tfjs')
// Load the binding (CPU computation)
require('@tensorflow/tfjs-node')
// Or load the binding (GPU computation)
require('@tensorflow/tfjs-node-gpu')

Loading TensorFlow Models

TensorFlow.js provides an NPM library (tfjs-models) to ease loading pre-trained & converted models for image classificationpose detection and k-nearest neighbours.

The MobileNet model used for image classification is a deep neural network trained to identify 1000 different classes.

In the project's README, the following example code is used to load the model.

import * as mobilenet from '@tensorflow-models/mobilenet';

// Load the model.
const model = await mobilenet.load();

One of the first challenges I encountered was that this does not work on Node.js.

Error: browserHTTPRequest is not supported outside the web browser.

Looking at the source code, the mobilenet library is a wrapper around the underlying tf.Model class. When the load() method is called, it automatically downloads the correct model files from an external HTTP address and instantiates the TensorFlow model.

The Node.js extension does not yet support HTTP requests to dynamically retrieve models. Instead, models must be manually loaded from the filesystem.

After reading the source code for the library, I managed to create a work-around...

Loading Models From a Filesystem

Rather than calling the module's load method, if the MobileNet class is created manually, the auto-generated path variable which contains the HTTP address of the model can be overwritten with a local filesystem path. Having done this, calling the load method on the class instance will trigger the filesystem loader class, rather than trying to use the browser-based HTTP loader.

const path = "mobilenet/model.json"
const mn = new mobilenet.MobileNet(1, 1);
mn.path = `file://${path}`
await mn.load()

Awesome, it works!

But how where do the models files come from?

MobileNet Models

Models for TensorFlow.js consist of two file types, a model configuration file stored in JSON and model weights in a binary format. Model weights are often sharded into multiple files for better caching by browsers.

Looking at the automatic loading code for MobileNet models, models configuration and weight shards are retrieved from a public storage bucket at this address.

https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v${version}_${alpha}_${size}/

The template parameters in the URL refer to the model versions listed here. Classification accuracy results for each version are also shown on that page.

According to the source code, only MobileNet v1 models can be loaded using the tensorflow-models/mobilenet library.

The HTTP retrieval code loads the model.json file from this location and then recursively fetches all referenced model weights shards. These files are in the format groupX-shard1of1.

Downloading Models Manually

Saving all model files to a filesystem can be achieved by retrieving the model configuration file, parsing out the referenced weight files and downloading each weight file manually.

I want to use the MobileNet V1 Module with 1.0 alpha value and image size of 224 pixels. This gives me the following URL for the model configuration file.

https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/model.json

Once this file has been downloaded locally, I can use the jq tool to parse all the weight file names.

$ cat model.json | jq -r ".weightsManifest[].paths[0]"
group1-shard1of1
group2-shard1of1
group3-shard1of1
...

Using the sed tool, I can prefix these names with the HTTP URL to generate URLs for each weight file.

$ cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https:\/\/storage.googleapis.com\/tfjs-models\/tfjs\/mobilenet_v1_1.0_224\//'
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group1-shard1of1
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group2-shard1of1
https://storage.googleapis.com/tfjs-models/tfjs/mobilenet_v1_1.0_224/group3-shard1of1
...

Using the parallel and curl commands, I can then download all of these files to my local directory.

cat model.json | jq -r ".weightsManifest[].paths[0]" | sed 's/^/https:\/\/storage.googleapis.com\/tfjs-models\/tfjs\/mobilenet_v1_1.0_224\//' |  parallel curl -O

Classifying Images

This example code is provided by TensorFlow.js to demonstrate returning classifications for an image.

const img = document.getElementById('img');

// Classify the image.
const predictions = await model.classify(img);

This does not work on Node.js due to the lack of a DOM.

The classify method accepts numerous DOM elements (canvas, video, image) and will automatically retrieve and convert image bytes from these elements into a tf.Tensor3D class which is used as the input to the model. Alternatively, the tf.Tensor3D input can be passed directly.

Rather than trying to use an external package to simulate a DOM element in Node.js, I found it easier to construct the tf.Tensor3D manually.

Generating Tensor3D from an Image

Reading the source code for the method used to turn DOM elements into Tensor3D classes, the following input parameters are used to generate the Tensor3D class.

const values = new Int32Array(image.height * image.width * numChannels);
// fill pixels with pixel channel bytes from image
const outShape = [image.height, image.width, numChannels];
const input = tf.tensor3d(values, outShape, 'int32');

pixels is a 2D array of type (Int32Array) which contains a sequential list of channel values for each pixel. numChannels is the number of channel values per pixel.

Creating Input Values For JPEGs

The jpeg-js library is a pure javascript JPEG encoder and decoder for Node.js. Using this library the RGB values for each pixel can be extracted.

const pixels = jpeg.decode(buffer, true);

This will return a Uint8Array with four channel values (RGBA) for each pixel (width * height). The MobileNet model only uses the three colour channels (RGB) for classification, ignoring the alpha channel. This code converts the four channel array into the correct three channel version.

const numChannels = 3;
const numPixels = image.width * image.height;
const values = new Int32Array(numPixels * numChannels);

for (let i = 0; i < numPixels; i++) {
  for (let channel = 0; channel < numChannels; ++channel) {
    values[i * numChannels + channel] = pixels[i * 4 + channel];
  }
}

MobileNet Models Input Requirements

The MobileNet model being used classifies images of width and height 224 pixels. Input tensors must contain float values, between -1 and 1, for each of the three channels pixel values.

Input values for images of different dimensions needs to be re-sized before classification. Additionally, pixels values from the JPEG decoder are in the range 0 - 255, rather than -1 to 1. These values also need converting prior to classification.

TensorFlow.js has library methods to make this process easier but, fortunately for us, the tfjs-models/mobilenet library automatically handles this issue! 👍

Developers can pass in Tensor3D inputs of type int32 and different dimensions to the classify method and it converts the input to the correct format prior to classification. Which means there's nothing to do... Super 🕺🕺🕺.

Obtaining Predictions

MobileNet models in Tensorflow are trained to recognise entities from the top 1000 classes in the ImageNet dataset. The models output the probabilities that each of those entities is in the image being classified.

The full list of trained classes for the model being used can be found in this file.

The tfjs-models/mobilenet library exposes a classify method on the MobileNet class to return the top X classes with highest probabilities from an image input.

const predictions = await mn_model.classify(input, 10);

predictions is an array of X classes and probabilities in the following format.

{
  className: 'panda',
  probability: 0.9993536472320557
}

Example

Having worked how to use the TensorFlow.js library and MobileNet models on Node.js, this script will classify an image given as a command-line argument.

source code

testing it out

npm install

wget http://bit.ly/2JYSal9 -O panda.jpg

node script.js mobilenet/model.json panda.jpg

If everything worked, the following output should be printed to the console.

classification results: [ {
    className: 'giant panda, panda, panda bear, coon bear',
    probability: 0.9993536472320557 
} ]

The image is correctly classified as containing a Panda with 99.93% probability! 🐼🐼🐼

Conclusion

TensorFlow.js brings the power of deep learning to JavaScript developers. Using pre-trained models with the TensorFlow.js library makes it simple to extend JavaScript applications with complex machine learning tasks with minimal effort and code.

Having been released as a browser-based library, TensorFlow.js has now been extended to work on Node.js, although not all of the tools and utilities support the new runtime. With a few days' hacking, I was able to use the library with the MobileNet models for visual recognition on images from a local file.

Getting this working in the Node.js runtime means I now move on to my next idea... making this run inside a serverless function! Come back soon to read about my next adventure with TensorFlow.js.

Originally published by** James Thomas** *at *dev.to

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

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

Learn More

☞ Machine Learning A-Z™: Hands-On Python & R In Data Science

☞ Python for Data Science and Machine Learning Bootcamp

☞ Machine Learning, Data Science and Deep Learning with Python

☞ [2019] Machine Learning Classification Bootcamp in Python

☞ Introduction to Machine Learning & Deep Learning in Python

☞ Machine Learning Career Guide – Technical Interview

☞ Machine Learning Guide: Learn Machine Learning Algorithms

☞ Machine Learning Basics: Building Regression Model in Python

☞ Machine Learning using Python - A Beginner’s Guide

MEAN Stack Angular 8 CRUD Web Application

MEAN Stack Angular 8 CRUD Web Application

In this tutorial, you’ll learn Angular 8 MEAN Stack tutorial and how to build an Angular 8 CRUD web application from MEAN scratch with MongoDB, Express js, Node js, and Angular Material UI library.

In this tutorial, you’ll learn Angular 8 MEAN Stack tutorial and how to build an Angular 8 CRUD web application from MEAN scratch with MongoDB, Express js, Node js, and Angular Material UI library.

In this MEAN stack tutorial, you’ll learn to set up a MEAN stack project from scratch. I’ll be creating back-end and front-end for a real-world CRUD web application from scratch.

For the demo purpose, I’ll create a students record management CRUD (create, read, update & delete) web application. In this CRUD app user will be able to perform the following tasks:

  • Add student ID
  • Add student name
  • Add student email
  • Add section Angular Material dropdown
  • Add multiple subjects using Angular material input chips
  • Add student’s gender using Angular material radio buttons
  • Add student date of birth using Angular material datepicker
Angular 8 Project Setup
  • Setting up Node js
  • Setting up Angular 8 CLI
  • Installing & setting up Angular 8 project
  • Creating routes to navigate between components
  • Creating Angular 8 service to manage CRUD operations
  • Consuming RESTful APIsusing Angular 8 Service
Angular Material UI Library
  • Setting up an Angular material ui library in a real-world Angular application.
  • Creating web application’s front-end using Angular material ui components like :- Angular material default theme, icons, buttons, navbar, date-picker, form, data tables and chip inputs.
MEAN Stack Back-end Setup
  • Set up MongoDB in Angular 8 MEAN stack app.
  • Setting up Expressjs server with Node js.
  • Creating RESTful APIs with Node js and Express js.

Table of Contents

  1. Angular 8 MEAN stack tutorial – Workflow of MEAN Stack Angular Material tutorial
  2. Angular 8 MEAN stack tutorial – Installing Node JS and Angular CLI
  3. Angular 8 MEAN stack tutorial – Angular 8 web app project setup
  4. Angular 8 MEAN stack tutorial – Setting up Angular 8 Routes to navigate between components.
  5. Angular 8 MEAN stack tutorial – Setting up Angular Material UI Library in Angular project.
  6. Angular 8 MEAN stack tutorial – Build Mean Stack Backend with MongoDB, Node JS and Express JS.
  7. Angular 8 MEAN stack tutorial – Build Angular 8 Service to Consume REST APIs.
  8. Angular 8 MEAN stack tutorial – Add Student using MEAN Stack REST APIs with Angular Material.
  9. Angular 8 MEAN stack tutorial – Show Students List and Delete Student Object.
  10. Angular 8 MEAN stack tutorial – Edit Students Object.
#1 Workflow of MEAN Stack Angular Material Tutorial

I’ll create application’s frontend in Angular 8 using Angular material 8 UI components and backend with Node js, Expressjs and MongoDB. To make it developer friendly I’ll create a separate project for frontend and backend. I will be building RESTful API using MEAN stack backend and will use those APIs with Angular service to consume the data.

Following technologies, will be used throughout the tutorial.

  • NPM v6.4.1
  • Node v10.15.3
  • RxJS V6.5.2
  • Angular v8.0.0
  • AngularCLI v8.0.0
  • MongoDB 4.0.6
  • MongoDB shell v4.0.6
#2 Installing Node JS and Angular CLI

Firstly, you need to have Node.js and Angular CLI installed in your system to work with Angular 8 Mean stack project. To install Node.js in your system

Node.js will help us to install the required dependencies for this Mean stack project.

In the next step, we’ll be installing Angular CLI with the help of NPM. Now with the help of Angular CLI, we’ll install the new Mean stack project.

npm install @angular/cli -g

We’ve successfully installed Node.js and Angular CLI by now. Now we can use the ng command to generate new Angular project, components, services, routing or many more features of Angular 8.

#3 Angular 8 web app project setup

We are going to build a MEAN stack web app using Angular 8. In our MEAN stack web app, we’ll use the Angular 8 framework to create the frontend of the app. Run the below command to generate a new angular project.

ng new angular8-meanstack-angular-material

Answer some Angular CLI questions:

# ? Would you like to add Angular routing? = Yes

# ? Which stylesheet format would you like to use? = CSS

Head over to the newly created Angular 8 project’s directory using below cmd.

cd angular8-meanstack-angular-material

In next step we’ll create three new components to manage Angular 8 Mean stack Angular CRUD app. Use Angular 8 CLI to generate Angular 8 components:

ng g component components/add-student --module app
ng g component components/edit-student --module app
ng g component components/students-list --module app

We are using --module app parameter because we have 2 module files in the app folder. Now with the –module app parameter We are telling Angular CLI that app.module.ts is our main app module file.

#4 Setting up Angular 8 Routes to navigate between components.

In this part of the tutorial we’ll create routes in our Mean stack angular 8 CRUD app. Routes allow us to navigate between components in Angular app.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { AddStudentComponent } from './components/add-student/add-student.component';
import { EditStudentComponent } from './components/edit-student/edit-student.component';
import { StudentsListComponent } from './components/students-list/students-list.component';
const routes: Routes = [
{ path: '', pathMatch: 'full', redirectTo: 'add-student' },
{ path: 'add-student', component: AddStudentComponent },
{ path: 'edit-student/:id', component: EditStudentComponent },
{ path: 'students-list', component: StudentsListComponent }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
#5 Setting up Angular Material UI Library in Angular project

We’ll be using Angular Material UI library to build students record management system. I will help you to create a beautiful responsive layout with Angular material ui components. We’ll create Mean stack Angular 8 CRUD app with following Angular material UI components:

  • Angular material default theme
  • Angular material datepicker
  • Angular material icons
  • Angular material buttons
  • Angular material navbar
  • Angular material form
  • Angular material data tables
  • Angular material chip inputs

Run the following command to setup Angular material.

ng add @angular/material

Choose the Angular material theme as per your choice:

? Choose a prebuilt theme name, or "custom" for a custom theme: Indigo/Pink
❯ Indigo/Pink        [ Preview: https://material.angular.io?theme=indigo-pink ] 
Deep Purple/Amber  [ Preview: https://material.angular.io?theme=deeppurple-amber ] 
Pink/Blue Grey     [ Preview: https://material.angular.io?theme=pink-bluegrey ] 
Purple/Green       [ Preview: https://material.angular.io?theme=purple-green ]

Then it will ask for Hammer.js (Gesture recognition support) and Angular browser animation support.

Select yes and hit enter.

Set up HammerJS for gesture recognition? (Y/n) = Y
? Set up browser animations for Angular Material? (Y/n) = Y

We’ve installed Angular material UI library in Angular 8 Mean stack project. Now we’ll create a separate material.module.ts file. In this file we’ll import the various Angular material service so that we can use it and manage centrally in our Angular 8 CRUD web app.

In next step we’ll create a custom Angular material module, Create src > app > material.module.ts file and import the following Angular material UI components in this file like given below.

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import {
MatButtonModule,
MatToolbarModule,
MatIconModule,
MatBadgeModule,
MatSidenavModule,
MatListModule,
MatGridListModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatRadioModule,
MatDatepickerModule,
MatNativeDateModule,
MatChipsModule,
MatTooltipModule,
MatTableModule,
MatPaginatorModule
} from '@angular/material';
@NgModule({
imports: [
CommonModule,
MatButtonModule,
MatToolbarModule,
MatIconModule,
MatSidenavModule,
MatBadgeModule,
MatListModule,
MatGridListModule,
MatFormFieldModule,
MatInputModule,
MatSelectModule,
MatRadioModule,
MatDatepickerModule,
MatNativeDateModule,
MatChipsModule,
MatTooltipModule,
MatTableModule,
MatPaginatorModule
],
exports: [
MatButtonModule,
MatToolbarModule,
MatIconModule,
MatSidenavModule,
MatBadgeModule,
MatListModule,
MatGridListModule,
MatInputModule,
MatFormFieldModule,
MatSelectModule,
MatRadioModule,
MatDatepickerModule,
MatChipsModule,
MatTooltipModule,
MatTableModule,
MatPaginatorModule
],
providers: [
MatDatepickerModule,
]
})
export class AngularMaterialModule { }

Go to app.module.ts file and import the AngularMaterialModule.

/* Angular material */
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { AngularMaterialModule } from './material.module';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
@NgModule({
declarations: [...],
imports: [
BrowserAnimationsModule,
AngularMaterialModule,
],
providers: [...],
bootstrap: [...],
schemas: [CUSTOM_ELEMENTS_SCHEMA]
})
export class AppModule { }

Setup a basic layout with Angular Material

Go to app.component.html file and include the following code.



Student Records


menu




<mat-sidenav #sidenav [mode]="isBiggerScreen() ? 'over' : 'side'" [(opened)]="opened" [fixedInViewport]="true"
[fixedTopGap]>


add Add Student


format_list_bulleted View Students









Add the following code in app.component.ts file.

import { Component, ViewChild, HostListener, OnInit } from '@angular/core';
import { MatSidenav } from '@angular/material/sidenav';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
opened = true;
@ViewChild('sidenav') sidenav: MatSidenav;
ngOnInit() {
console.log(window.innerWidth)
if (window.innerWidth < 768) {
this.sidenav.fixedTopGap = 55;
this.opened = false;
} else {
this.sidenav.fixedTopGap = 55;
this.opened = true;
}
}
@HostListener('window:resize', ['$event'])
onResize(event) {
if (event.target.innerWidth < 768) {
this.sidenav.fixedTopGap = 55;
this.opened = false;
} else {
this.sidenav.fixedTopGap = 55
this.opened = true;
}
}
isBiggerScreen() {
const width = window.innerWidth || document.documentElement.clientWidth || document.body.clientWidth;
if (width < 768) {
return true;
} else {
return false;
}
}
}

To set up the style add the following code in styles.css file.

html,body{height:100%;}
body{margin:0;font-family:'Roboto', sans-serif;}
.header{justify-content:space-between;}
.user-profile{margin-left:15px;}
.mat-sidenav-container{height:100%;display:flex;flex:1 1 auto;}
.mat-nav-list .mat-list-item{font-size:15px;}
.nav-tool-items{display:inline-block;margin-right:13px;}
.user-profile{margin-left:15px;cursor:pointer;}
.hamburger{visibility:hidden !important;}
.mat-sidenav,.mat-sidenav-content{padding:15px;}
.mat-list-item.active{background:rgba(0, 0, 0, .04);}
.mat-sidenav-content{padding:25px 40px 0;}
.mat-sidenav{background-color:#F2F2F2;width:250px;}
.header{position:sticky;position:-webkit-sticky;top:0;z-index:1000;}
mat-sidenav mat-icon{margin-right:12px;}
.hamburger{margin-top:5px;cursor:pointer;}
.mat-radio-button,.mat-radio-group{margin-right:25px;}
.controlers-wrapper>*{width:100%;padding:0;}
.misc-bottom-padding{margin:8px 0 10px;}
.misc-bottom-padding mat-label{margin-right:15px;}
mat-radio-group mat-radio-button{margin-left:5px;}
.button-wrapper button{margin-right:5px;}
table.mat-table,table{width:100%;}
.inner-wrapper{padding:15px 0 130px;width:100%;}
.inner-wrapper mat-card{display:inline-block;margin:0 6% 0 0;vertical-align:top;width:44%;}
.full-wrapper{width:100%;}
.multiple-items{position:relative;}
.multiple-items .tooltip-info{right:0;top:7px;cursor:pointer;color:#a1a7c7;position:absolute;font-size:20px;}
body .push-right{margin-right:10px;}
.no-data{text-align:center;padding-top:30px;color:#6c75a9;}
.button-wrapper{margin:20px 0 0 0;}
@media (max-width:1024px){.inner-wrapper mat-card{width:100%;}
.mat-sidenav-content{padding:20px 20px 0;}
.misc-bottom-padding mat-label{display:block;padding-bottom:10px;}
.mat-sidenav{width:230px;}
.mat-nav-list .mat-list-item{font-size:14px;}
}
@media (max-width:767px){.nav-tool-items{margin-right:0;}
.hamburger{visibility:visible !important;}
}

Your basic layout is ready ready with Angular material library, in next step we’ll set up backend using node js, express js and mongoDB.

#6 Build Mean Stack Backend with MongoDB, NodeJS and ExpressJS

In this part of the tutorial, we are going to build a robust Mean stack backend using mongoDB, nodejs, and expressjs.

Following topics will be covered in this part of the tutorial:

  • Create a separate project for Angular 8 Mean stack backend.
  • Install required dependencies using NPM: body-parser, cors, express js, mongoose, and nodemon.
  • Set up MongoDB Database connection in Mean stack app to access MongoDB database using MongoDB Shell.
  • Define a data model with mongoose JS in Mean stack project.
  • Create RESTful APIs with Express js Routes in Mean Stack Angular 8 Project.
  • Configure Angular 8 Mean Stack backend

Create a separate project for Angular 8 Mean stack backend.

In order to set up a separate Mean stack backend create a folder by the name of backend in the Angular’s root directory.

mkdir backend && cd backend

You’ve created the backend folder and entered into the project.

Next thing is to create a separate package.json for your Mean stack backend.

npm init

Install required dependencies using NPM: body-parser, cors, express js, mongoose, and nodemon.

After that install the required dependencies for your Angular 8 Mean stack app.

npm install --save express mongoose cors body-parser

Then install nodemon package it will save us from restarting the server every-time we make the changes in our backend code.

npm install nodemon --save-dev

Your package.json file for Angular 8 Mean stack backend will look something like this.

{
"name": "angular8-meanstack-backend",
"version": "1.0.0",
"description": "An angular 8 mean stack crud web app with angular material 8.",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Digamber Rawat",
"license": "ISC",
"dependencies": {
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"mongoose": "^5.5.11"
},
"devDependencies": {
"nodemon": "^1.19.1"
}
}

Set up MongoDB Database connection in Mean stack app to access MongoDB database using MongoDB Shell.

To setup the MongoDB database connection within the Mean stack app, we need to create a folder by the name of database and create a file db.js there. Run the given below command.

mkdir database && cd database && touch db.js

Inside the backend > database > db.js file paste the following code. Here angular8mean is your mongoDB database name.

module.exports = {
db: 'mongodb://localhost:27017/angular8mean'
};

Define Student data model with mongoose JS in Mean stack app.

We’ll create a model folder, inside the model folder we’ll create a Student Schema for students collection in MongoDB. Paste the below code in the model > Student.js file.

const mongoose = require('mongoose');
const Schema = mongoose.Schema;
// Define collection and schema
let Student = new Schema({
student_name: {
type: String
},
student_email: {
type: String
},
section: {
type: String
},
subjects: {
type: Array
},
gender: {
type: String
},
dob: {
type: Date
}
}, {
collection: 'students'
})
module.exports = mongoose.model('Student', Student)

Create RESTful APIs with Express js Routes in Mean Stack Angular 8 Project.

In this Angular 8 Mean stack tutorial we are going to create RESTful APIs using Express js and Node js. I will create a routes folder inside the backend folder and create a student.routes.js file.

Enter the below command to create the routes folder and student.routes.js file.

mkdir routes && cd routes && touch student.route.js

We’ve created RESTful APIs using Express js and Student Model, now Go to student.route.js file and add the following code.

const express = require('express');
const app = express();
const studentRoute = express.Router();
// Student model
let Student = require('../model/Student');
// Add Student
studentRoute.route('/add-student').post((req, res, next) => {
Student.create(req.body, (error, data) => {
if (error) {
return next(error)
} else {
res.json(data)
}
})
});
// Get all student
studentRoute.route('/').get((req, res) => {
Student.find((error, data) => {
if (error) {
return next(error)
} else {
res.json(data)
}
})
})
// Get single student
studentRoute.route('/read-student/:id').get((req, res) => {
Student.findById(req.params.id, (error, data) => {
if (error) {
return next(error)
} else {
res.json(data)
}
})
})
// Update student
studentRoute.route('/update-student/:id').put((req, res, next) => {
Student.findByIdAndUpdate(req.params.id, {
$set: req.body
}, (error, data) => {
if (error) {
return next(error);
console.log(error)
} else {
res.json(data)
console.log('Student successfully updated!')
}
})
})
// Delete student
studentRoute.route('/delete-student/:id').delete((req, res, next) => {
Student.findByIdAndRemove(req.params.id, (error, data) => {
if (error) {
return next(error);
} else {
res.status(200).json({
msg: data
})
}
})
})
module.exports = studentRoute;

Configure Angular 8 Mean Stack backend

Now we’ll create app.js file in backend folder’s root. Run the below command to generate backend > app.js file.

touch app.js

Mange Backend settings in Mean stack Project.

Now we are going to create app.js file this file will hold the core logic of our Mean stack project’s backend logic. This file will manage the following things.

  • Setup port using express.
  • Setup 404 error using express js.
  • Making mongoDB database connection
  • Serving static files using express js in Mean stack app.
  • Handling errors using Express js in Angular 8 Mean stack project.
let express = require('express'),
path = require('path'),
mongoose = require('mongoose'),
cors = require('cors'),
bodyParser = require('body-parser'),
dataBaseConfig = require('./database/db');
// Connecting mongoDB
mongoose.Promise = global.Promise;
mongoose.connect(dataBaseConfig.db, {
useNewUrlParser: true
}).then(() => {
console.log('Database connected sucessfully ')
},
error => {
console.log('Could not connected to database : ' + error)
}
)
// Set up express js port
const studentRoute = require('../backend/routes/student.route')
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({
extended: false
}));
app.use(cors());
app.use(express.static(path.join(__dirname, 'dist/angular8-meanstack-angular-material')));
app.use('/', express.static(path.join(__dirname, 'dist/angular8-meanstack-angular-material')));
app.use('/api', studentRoute)
// Create port
const port = process.env.PORT || 4000;
const server = app.listen(port, () => {
console.log('Connected to port ' + port)
})
// Find 404 and hand over to error handler
app.use((req, res, next) => {
next(createError(404));
});
// error handler
app.use(function (err, req, res, next) {
console.error(err.message);
if (!err.statusCode) err.statusCode = 500;
res.status(err.statusCode).send(err.message);
});

Everything has been placed at its place, now we have to start the Angular 8 project, mongoDB server and Nodemon server.

Run the following commands…

Start the Angular project:

ng serve

Initialise the mongoDB database:

cd backend && mongod

Start the nodemon server:

cd backend && nodemon

I hope your Angular 8 Mean stack backend server is running fine, you can check your frontend and backend on the following URLs:

Angular frontend URL:

http://localhost:4200

MEAN stack backend URL:

http://localhost:4000/api

MEAN stack RESTful APIs using Express JS

We can hit the below command in the terminal to check out how our newly created RESTful APIs are working.

curl -i -H "Accept: application/json" localhost:4000/api
# HTTP/1.1 200 OK
# X-Powered-By: Express
# Access-Control-Allow-Origin: *
# Content-Type: application/json; charset=utf-8
# Content-Length: 58
# ETag: W/"3a-dzxOuKmgt3HAevjaPlycYSK+FhI"
# Date: Sun, 26 May 2019 18:53:03 GMT
# Connection: keep-alive

If we are getting this type of response that means we are ready to go with our APIs. Or similarly we can also use Postmen API development environment tool to test our RESTful APIs.

#7 Build Angular 8 Service to Consume REST APIs

To create Angular 8 Mean stack student records management system app. We need to create a service file where we’ll consume REST APIs to manage the student data. This service file will manage the Create, Read, Update and Delete operations.

Configure Angular 8 HttpClientModule:

Import HttpClientModule service in app.module.ts file.

/* Angular 8 http service */
import { HttpClientModule } from '@angular/common/http';
@NgModule({
imports: [
HttpClientModule
]
})

Create & configure Student class:

Enter the below command to create model > student.ts file.

export class Student {
_id: String;
student_name: String;
student_email: String;
section: String;
subjects: Array;
dob: Date;
gender: String;
}
Create Angular 8 service to Consume REST APIs

Enter the following command to create Angular 8 service to manage CRUD operations in Angular 8 MEAN Stack web app.

ng g s shared/api

In the given below code we’ve consumed REST APIs using Angular 8 service. Add the following code in your shared > api.service.ts file.

import { Injectable } from '@angular/core';
import { Student } from './student';
import { Observable, throwError } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { HttpClient, HttpHeaders, HttpErrorResponse } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class ApiService {
endpoint: string = 'http://localhost:4000/api';
headers = new HttpHeaders().set('Content-Type', 'application/json');
constructor(private http: HttpClient) { }
// Add student
AddStudent(data: Student): Observable {
let API_URL = `${this.endpoint}/add-student`;
return this.http.post(API_URL, data)
.pipe(
catchError(this.errorMgmt)
)
}
// Get all students
GetStudents() {
return this.http.get(`${this.endpoint}`);
}
// Get student
GetStudent(id): Observable {
let API_URL = `${this.endpoint}/read-student/${id}`;
return this.http.get(API_URL, { headers: this.headers }).pipe(
map((res: Response) => {
return res || {}
}),
catchError(this.errorMgmt)
)
}
// Update student
UpdateStudent(id, data: Student): Observable {
let API_URL = `${this.endpoint}/update/${id}`;
return this.http.put(API_URL, data, { headers: this.headers }).pipe(
catchError(this.errorMgmt)
)
}
// Delete student
DeleteStudent(id): Observable {
var API_URL = `${this.endpoint}/delete-student/${id}`;
return this.http.delete(API_URL).pipe(
catchError(this.errorMgmt)
)
}
// Error handling 
errorMgmt(error: HttpErrorResponse) {
let errorMessage = '';
if (error.error instanceof ErrorEvent) {
// Get client-side error
errorMessage = error.error.message;
} else {
// Get server-side error
errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
}
console.log(errorMessage);
return throwError(errorMessage);
}
}

Go to app.module.ts file and import this API service like given below.

/* Angular 8 CRUD services */
import { ApiService } from './shared/api.service';
@NgModule({
providers: [ApiService]
})
#8 Add Student using MEAN Stack REST APIs with Angular Material

In this part of the tutorial we will learn to add student in the MongoDB database. We’ll be using Angular 8 Reactive form to add student in the database.

Import ReactiveFormsModule API in App Module File

In order to work with Angular 8 Reactive Forms we must import the ReactiveFormsModule API and FormsModule API in app.module.ts file.

/* Reactive form services in Angular 8 */
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
@NgModule({
imports: [
ReactiveFormsModule,
FormsModule
],
})
export class AppModule { }

Go to add-student.component.ts file and include the given below code.

import { Router } from '@angular/router';
import { Component, OnInit, ViewChild, NgZone } from '@angular/core';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material';
import { ApiService } from './../../shared/api.service';
import { FormGroup, FormBuilder, Validators } from "@angular/forms";
export interface Subject {
name: string;
}
@Component({
selector: 'app-add-student',
templateUrl: './add-student.component.html',
styleUrls: ['./add-student.component.css']
})
export class AddStudentComponent implements OnInit {
visible = true;
selectable = true;
removable = true;
addOnBlur = true;
@ViewChild('chipList') chipList;
@ViewChild('resetStudentForm') myNgForm;
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
studentForm: FormGroup;
subjectArray: Subject[] = [];
SectioinArray: any = ['A', 'B', 'C', 'D', 'E'];
ngOnInit() {
this.submitBookForm();
}
constructor(
public fb: FormBuilder,
private router: Router,
private ngZone: NgZone,
private studentApi: ApiService
) { }
/* Reactive book form */
submitBookForm() {
this.studentForm = this.fb.group({
student_name: ['', [Validators.required]],
student_email: ['', [Validators.required]],
section: ['', [Validators.required]],
subjects: [this.subjectArray],
dob: ['', [Validators.required]],
gender: ['Male']
})
}
/* Add dynamic languages */
add(event: MatChipInputEvent): void {
const input = event.input;
const value = event.value;
// Add language
if ((value || '').trim() && this.subjectArray.length < 5) {
this.subjectArray.push({ name: value.trim() })
}
// Reset the input value
if (input) {
input.value = '';
}
}
/* Remove dynamic languages */
remove(subject: Subject): void {
const index = this.subjectArray.indexOf(subject);
if (index >= 0) {
this.subjectArray.splice(index, 1);
}
}  
/* Date */
formatDate(e) {
var convertDate = new Date(e.target.value).toISOString().substring(0, 10);
this.studentForm.get('dob').setValue(convertDate, {
onlyself: true
})
}  
/* Get errors */
public handleError = (controlName: string, errorName: string) => {
return this.studentForm.controls[controlName].hasError(errorName);
}  
/* Submit book */
submitStudentForm() {
if (this.studentForm.valid) {
this.studentApi.AddStudent(this.studentForm.value).subscribe(res => {
this.ngZone.run(() => this.router.navigateByUrl('/students-list'))
});
}
}
}

Then go to add-student.component.html file and add the following code.



# Add Student













You must provide a**student name**






You must provide a**student email**




Section

{{sectioinArray}}



Section is required










<mat-chip *ngFor="let subjectArray of subjectArray" [selectable]="selectable" [removable]="removable"
(removed)="remove(subjectArray)">
{{subjectArray.name}}
cancel

<input placeholder="Add subject" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)">


info




<input matInput readonly [matDatepicker]="picker" placeholder="Date of birth" formControlName="dob"
(dateChange)="formatDate($event)">



Date of birth is required




Gender:

Male
Female








Submit





#9 Show Students List and Delete Student Object

Go to students-list.component.ts file and add the given below code. In this file, we’ll manage the following tasks.

  • Implement the Angular material data tables and Pagination with Angular 8 Mean stack project.
  • Render Students List using Mean stack REST APIs
  • Delete Single Object using REST APIs in Angular 8 Mean stack app
import { Student } from './../../shared/student';
import { ApiService } from './../../shared/api.service';
import { Component, ViewChild, OnInit } from '@angular/core';
import { MatPaginator, MatTableDataSource } from '@angular/material';
@Component({
selector: 'app-students-list',
templateUrl: './students-list.component.html',
styleUrls: ['./students-list.component.css']
})
export class StudentsListComponent implements OnInit {
StudentData: any = [];
dataSource: MatTableDataSource;
@ViewChild(MatPaginator) paginator: MatPaginator;
displayedColumns: string[] = ['_id', 'student_name', 'student_email', 'section', 'action'];
constructor(private studentApi: ApiService) {
this.studentApi.GetStudents().subscribe(data => {
this.StudentData = data;
this.dataSource = new MatTableDataSource(this.StudentData);
setTimeout(() => {
this.dataSource.paginator = this.paginator;
}, 0);
})    
}
ngOnInit() { }
deleteStudent(index: number, e){
if(window.confirm('Are you sure')) {
const data = this.dataSource.data;
data.splice((this.paginator.pageIndex * this.paginator.pageSize) + index, 1);
this.dataSource.data = data;
this.studentApi.DeleteStudent(e._id).subscribe()
}
}
}

Now, go to students-list.component.html file and include the following code.



# Students List




There is no student added yet!

 0">



 Student ID 
 {{element._id}} 


 Student Name 
 {{element.student_name}} 


 Email 
 {{element.student_email}} 


 Section 
 {{element.section}} 


 Action 

<button mat-raised-button color="primary" class="push-right"
[routerLink]="['/edit-student/', element._id]">Edit
Delete








#10 Edit Students Object in Mean Stack App

We are going to create edit functionality using RESTful API in Mean stack app with Angular Material 8.

Go to edit-list.component.ts file and add the following code.

import { Router, ActivatedRoute } from '@angular/router';
import { Component, OnInit, ViewChild, NgZone } from '@angular/core';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { MatChipInputEvent } from '@angular/material';
import { ApiService } from './../../shared/api.service';
import { FormGroup, FormBuilder, Validators } from "@angular/forms";
export interface Subject {
name: string;
}
@Component({
selector: 'app-edit-student',
templateUrl: './edit-student.component.html',
styleUrls: ['./edit-student.component.css']
})
export class EditStudentComponent implements OnInit {
visible = true;
selectable = true;
removable = true;
addOnBlur = true;
@ViewChild('chipList') chipList;
@ViewChild('resetStudentForm') myNgForm;
readonly separatorKeysCodes: number[] = [ENTER, COMMA];
studentForm: FormGroup;
subjectArray: Subject[] = [];
SectioinArray: any = ['A', 'B', 'C', 'D', 'E'];
ngOnInit() {
this.updateBookForm();
}
constructor(
public fb: FormBuilder,
private router: Router,
private ngZone: NgZone,
private actRoute: ActivatedRoute,
private studentApi: ApiService
) { 
var id = this.actRoute.snapshot.paramMap.get('id');
this.studentApi.GetStudent(id).subscribe(data => {
console.log(data.subjects)
this.subjectArray = data.subjects;
this.studentForm = this.fb.group({
student_name: [data.student_name, [Validators.required]],
student_email: [data.student_email, [Validators.required]],
section: [data.section, [Validators.required]],
subjects: [data.subjects],
dob: [data.dob, [Validators.required]],
gender: [data.gender]
})      
})    
}
/* Reactive book form */
updateBookForm() {
this.studentForm = this.fb.group({
student_name: ['', [Validators.required]],
student_email: ['', [Validators.required]],
section: ['', [Validators.required]],
subjects: [this.subjectArray],
dob: ['', [Validators.required]],
gender: ['Male']
})
}
/* Add dynamic languages */
add(event: MatChipInputEvent): void {
const input = event.input;
const value = event.value;
// Add language
if ((value || '').trim() && this.subjectArray.length < 5) {
this.subjectArray.push({ name: value.trim() })
}
// Reset the input value
if (input) {
input.value = '';
}
}
/* Remove dynamic languages */
remove(subject: Subject): void {
const index = this.subjectArray.indexOf(subject);
if (index >= 0) {
this.subjectArray.splice(index, 1);
}
}
/* Date */
formatDate(e) {
var convertDate = new Date(e.target.value).toISOString().substring(0, 10);
this.studentForm.get('dob').setValue(convertDate, {
onlyself: true
})
}
/* Get errors */
public handleError = (controlName: string, errorName: string) => {
return this.studentForm.controls[controlName].hasError(errorName);
}
/* Update book */
updateStudentForm() {
console.log(this.studentForm.value)
var id = this.actRoute.snapshot.paramMap.get('id');
if (window.confirm('Are you sure you want to update?')) {
this.studentApi.UpdateStudent(id, this.studentForm.value).subscribe( res => {
this.ngZone.run(() => this.router.navigateByUrl('/students-list'))
});
}
}
}

Now go to edit-list.component.html file and add the following code.



# Add Student













You must provide a**student name**






You must provide a**student email**




Section

{{sectioinArray}}



Section is required










<mat-chip *ngFor="let subjectArray of subjectArray" [selectable]="selectable" [removable]="removable"
(removed)="remove(subjectArray)">
{{subjectArray.name}}
cancel

<input placeholder="Add subject" [matChipInputFor]="chipList"
[matChipInputSeparatorKeyCodes]="separatorKeysCodes" [matChipInputAddOnBlur]="addOnBlur"
(matChipInputTokenEnd)="add($event)">


info




<input matInput readonly [matDatepicker]="picker" placeholder="Date of birth" formControlName="dob"
(dateChange)="formatDate($event)">



Date of birth is required




Gender:

Male
Female








Update





Conclusion

Finally, we have created a basic Angular 8 MEAN stack CRUD web app with Angular Material. Anyhow, if we have missed anything you can check out GitHub repo of this project.