Monday, April 13, 2020

separate admin and the front web in angular 7 project

in any web application/project we need admin & front-end code, in angular 2, Angular 4, Angular 5, Angular 6, Angular 7, etc project you can run the build of a particular panel. In my case, I wanted to create a project with multiple apps/panel that I use in my MEAN STACK application/project.

The file angular.json has the projects section with the property main, and to run a particular app/project angular.json to point at a particular project, e.g. "site" to run and make a separate build for web and "admin" run and make a separate build for admin panel .

By separating the admin & front-end code, so that the client's app.js is free of any admin code or it's dependencies, and then there could be "admin.js" and "admin-vendor.js" files for the admin.

The main advantage of this By separating the admin & front-end code is that
-> The File Size will be made too small because of the separate build.
->Security made more powerful because admin build/source code will be private to another folder.

Here we will make simple demo by using MEAN stack means by using NODEJS, ANGULAR 7, MONGODB, MOGOOSE,EXPRESSJS.

=>Directory structure

From the above directory structure It's clear that
->admin_src
   This folder/directory for put all admin side front-end code files and directory like as index.html, assets, app to add styles, javascript, components, images, modules etc.

->admin_dist
   Put  admin_src code's build code in folder/directory by using Angular CLI

->src
   This folder/directory for put all client side front-end code files and directory like as index.html, assets, app to add styles, javascript, components, images, modules etc.

->dist
   Put  src code's build code in folder/directory by using Angular CLI

->model
   In this we can put server-side / back-end API code and database and models. This is for server-side logic and configuration

 ->app.js
     This is the main file to run nodeJs server code and start a nodeJs project

Here is some files and code which is most important is given below,

angular.json
{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "site": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "dist",
            "index": "src/index.html",
            "main": "src/main.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.app.json",
            "assets": [
              "src/assets",
              "src/favicon.ico"
            ],
            "styles": [
                "assets/css/styles.scss"
            ],
            "scripts": [],
            "es5BrowserSupport": true
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "site:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "site:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "site:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "src/test.ts",
            "polyfills": "src/polyfills.ts",
            "tsConfig": "src/tsconfig.spec.json",
            "karmaConfig": "src/karma.conf.js",
            "assets": [
              "src/assets",
              "src/favicon.ico"
            ],
            "styles": [
                "assets/css/styles.scss"
            ],
            "scripts": [],
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "src/tsconfig.app.json",
              "src/tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    },
    "site-e2e": {
      "root": "e2e/",
      "projectType": "application",
      "prefix": "",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "site:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "site:serve:production"
            }
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": "e2e/tsconfig.e2e.json",
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    },
    "admin": {
      "root": "",
      "sourceRoot": "admin_src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {
            "outputPath": "admin_dist",
            "index": "admin_src/index.html",
            "main": "admin_src/main.ts",
            "polyfills": "admin_src/polyfills.ts",
            "tsConfig": "admin_src/tsconfig.app.json",
            "assets": [
              "admin_src/assets",
              "admin_src/favicon.ico"
            ],
            "styles": [
              "styles.css"
            ],
            "scripts": [],
            "es5BrowserSupport": true
          },
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "admin_src/environments/environment.ts",
                  "with": "admin_src/environments/environment.prod.ts"
                }
              ],
              "optimization": true,
              "outputHashing": "all",
              "sourceMap": false,
              "extractCss": true,
              "namedChunks": false,
              "aot": true,
              "extractLicenses": true,
              "vendorChunk": false,
              "buildOptimizer": true,
              "budgets": [
                {
                  "type": "initial",
                  "maximumWarning": "2mb",
                  "maximumError": "5mb"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "admin:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "admin:build:production"
            }
          }
        },
        "extract-i18n": {
          "builder": "@angular-devkit/build-angular:extract-i18n",
          "options": {
            "browserTarget": "admin:build"
          }
        },
        "test": {
          "builder": "@angular-devkit/build-angular:karma",
          "options": {
            "main": "admin_src/test.ts",
            "polyfills": "admin_src/polyfills.ts",
            "tsConfig": "admin_src/tsconfig.spec.json",
            "karmaConfig": "admin_src/karma.conf.js",
            "assets": [
              "admin_src/assets",
              "admin_src/favicon.ico"
            ],
            "styles": [
              "styles.css"
            ],
            "scripts": []
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": [
              "admin_src/tsconfig.app.json",
              "admin_src/tsconfig.spec.json"
            ],
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    },
    "admin-e2e": {
      "root": "e2e/",
      "projectType": "application",
      "prefix": "",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "e2e/protractor.conf.js",
            "devServerTarget": "admin:serve"
          },
          "configurations": {
            "production": {
              "devServerTarget": "admin:serve:production"
            }
          }
        },
        "lint": {
          "builder": "@angular-devkit/build-angular:tslint",
          "options": {
            "tsConfig": "e2e/tsconfig.e2e.json",
            "exclude": [
              "**/node_modules/**"
            ]
          }
        }
      }
    }
  },
  "defaultProject": "site"
}

->Under app arrray, "name": "site" is configure for client side's front-end
->Under app arrray, "name": "admin" is configure for admin side's front-end
This will be used in angular cli at build time which is described below.

app.js
// set up =============================
var express  = require('express');
var app      = express();    // create our app w/ express
var path = require('path');
var mongoose = require('mongoose');  // mongoose for mongodb
var port       = process.env.PORT || 3000; // set the port

var morgan = require('morgan'); // log requests to the console (express4)
var bodyParser = require('body-parser'); // pull information from HTML POST (express4)
var expressSession = require('express-session');
var cookieParser = require('cookie-parser'); // the session is stored in a cookie, so we use this to parse it
var methodOverride = require('method-override'); // simulate DELETE and PUT (express4)

// configuration =========================
var mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
mongoose.connect('mongodb://localhost/mean-angular5', {promiseLibrary: require('bluebird') })
  .then(() =>  console.log('connection succesful'))
  .catch((err) => console.error(err));

// must use cookieParser before expressSession
app.use(cookieParser());
app.use(expressSession({secret:'somesecrettokenhere',resave: true,saveUninitialized: true}));

app.use("/root", express.static(path.resolve("/")));
app.use("/dist", express.static(path.resolve("dist")));
app.use("/admin_dist", express.static(path.resolve("admin_dist")));

app.use(morgan('dev')); // log every request to the console
app.use(bodyParser.urlencoded({'extended':'true'})); // parse application/x-www-form-urlencoded
app.use(bodyParser.json());                                     // parse application/json
app.use(bodyParser.json({ type: 'application/vnd.api+json' })); // parse application/vnd.api+json as json
app.use(methodOverride());
// routes ================================
app.use(require('./src/routes/book')); //Include here all routing files / controller file

/**
     *@description Set default template for / route, direct access form url, refresh page
     *All except resize/, api/, vendor/, css/, admin matching string
     *
     */
    app.get(/^\/(?!.*api\/.*|.*resize\/.*|.*vendor\/.*|.*css\/.*|.*admin.*).*$/, function(req, res, err) {
        console.log("req.headers.host=" + req.headers.host);
        console.log("req.url=" + req.url);
        console.log("Index file called-----------------------------------------------site");
        console.log("site else path=" + path.resolve(__dirname + "/dist/index.html"));
        res.sendFile(path.resolve(__dirname + "/dist/index.html"));
    });
 
    /**
     *@description Set default admin template for /admin*(any after admin or admin) route
     */
    app.get("/admin*", function(req, res) {
        console.log("Index file called-----------------------------------------------admin");
        console.log("admin path=" + path.resolve(__dirname + "/admin_dist/index.html"));
        res.sendFile(path.resolve(__dirname + "/admin_dist/index.html"));
    });

// listen (start app with node server.js) ======================
app.listen(port);
console.log("App listening on port " + port);

In 'app.js' most important code is
app.use("/root", express.static(path.resolve("/")));
app.use("/dist", express.static(path.resolve("dist")));
app.use("/admin_dist", express.static(path.resolve("admin_dist")));

app.get(/^\/(?!.*api\/.*|.*resize\/.*|.*vendor\/.*|.*css\/.*|.*admin.*).*$/, function(req, res, err) {
        console.log("req.headers.host=" + req.headers.host);
        console.log("req.url=" + req.url);
        console.log("Index file called-----------------------------------------------site");
        console.log("site else path=" + path.resolve(__dirname + "/dist/index.html"));
        res.sendFile(path.resolve(__dirname + "/dist/index.html"));
    });

app.get("/admin*", function(req, res) {
        console.log("Index file called-----------------------------------------------admin");
        console.log("admin path=" + path.resolve(__dirname + "/admin_dist/index.html"));
        res.sendFile(path.resolve(__dirname + "/admin_dist/index.html"));
    });

/root, /dist and /admin_dist static path is used to point root, dist and admin_dist directory so from front-end link path can be start form /root, /dist and /admin_dist.

->when user refresh or open directly from client side index.html from src will run
->when user refresh or open directly from admin side index.html from admin_dist will run

=>how to create a module using angular cli
ng g m module_name --project site
ng g m module_name --project admin

=>how to create a component using angular cli
ng g c test_1 --project site
ng g c test_1 --project admin

=>how to build using angular cli
ng build --project site --deploy-url="/dist/" --watch
ng build --project admin --deploy-url="/admin_dist/" --watch

some most important options in above are as below.
--project
   by using this option angular cli can determine that which folder/directory takes for build and in which folder/directory to be put.
      here, "--project site" is for make build from src folder and put it in dist folder, and "--project admin" is for make build from admin_src folder and put it in admin_dist folder.
      You can use any with --project option, which is defined in angular.json configuration files apps name.

--deploy-url
    This option is use for add default path before all bundle link (index.html)
    ex :
    1)Without using --deploy-url="/admin_dist/"  it will put in admin_dist/index.html
    <script type="text/javascript" src="main.bundle.js">

    2)Using --deploy-url="/admin_dist/"  it will put in admin_dist/index.html
    <script type="text/javascript" src="/admin_dist/main.bundle.js">

--watch
       This option is used to auto build on code change.

By using Angular CLI: multiple apps in the same project it's easy and more usable for the web developer which application developed in angular.

I hope you can use it easy but if you have any confusion about it you can write your query in the comment box.

If you are using angular 4 project then please read following link because angular 6 and next versions of angualars migrate from .angular-cli.json to angular.json so for app laying angular 4 has come changes
separate admin and front web in angular project 4

1 comment:

  1. Thanks for sharing this wonderful content.its very useful to us.I gained many unknown information, the way you have clearly explained is really fantastic.keep posting such useful information.
    oracle training in chennai

    oracle training institute in chennai

    oracle training in bangalore

    oracle training in hyderabad

    oracle training

    hadoop training in chennai

    hadoop training in bangalore





    ReplyDelete