Saturday, October 27, 2018

separate admin and the front web in angular 4 project

in any web application/project we need admin & front-end code, in angular 2, Angular 4, Angular 5, Angular 6, 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-cli.json has the apps section with the property main, and to run a particular app. angular-cli.json to point at a particular app, 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 4, MONGODB, MOGOOSE,EXPRESSJS.

If you are using angular 6 or angular 6+  project's version then please read following link because angular 6 and next versions of angualar migrate from .angular-cli.json to angular.json so for app laying angular 6+ check and use following link

separate admin and front web in angular project 6+

For lover version of angular please continue reading


=>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-cli.json
{   "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "project": {
    "name": "myProjectName"
  },
  "apps": [
    {
      "name": "site",
      "root": "src",
      "outDir": "dist",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "assets/css/styles.scss"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    },
    {
      "name": "admin",
      "root": "admin_src",
      "outDir": "admin_dist",
      "assets": [
        "assets",
        "favicon.ico"
      ],
      "index": "index.html",
      "main": "main.ts",
      "polyfills": "polyfills.ts",
      "test": "test.ts",
      "tsconfig": "tsconfig.app.json",
      "testTsconfig": "tsconfig.spec.json",
      "prefix": "app",
      "styles": [
        "styles.css"
      ],
      "scripts": [],
      "environmentSource": "environments/environment.ts",
      "environments": {
        "dev": "environments/environment.ts",
        "prod": "environments/environment.prod.ts"
      }
    }
  ],
  "e2e": {
    "protractor": {
      "config": "./protractor.conf.js"
    }
  },
  "lint": [
    {
      "project": "src/tsconfig.app.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "src/tsconfig.spec.json",
      "exclude": "**/node_modules/**"
    },
    {
      "project": "e2e/tsconfig.e2e.json",
      "exclude": "**/node_modules/**"
    }
  ],
  "test": {
    "karma": {
      "config": "./karma.conf.js"
    }
  },
  "defaults": {
    "styleExt": "css",
    "component": {}
  }
}

->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 --app site
ng g m module_name --app admin

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

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

some most important options in above are as below.
--app
   by using this option angular cli can determine that which folder/directory takes for build and in which folder/directory to be put.
      here, "--app site" is for make build from src folder and put it in dist folder, and "--app admin" is for make build from admin_src folder and put it in admin_dist folder.
      You can use any with --app option, which is defined in .angular-cli.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.

2 comments:

  1. How to create routing between admin a d client front end with this setup? I created separate routing files for admin and client and put them in their respective locations. What should I do to create a route like localhist/admin to open the admin index.html file

    ReplyDelete
    Replies
    1. Sorry for the late answer.
      It depends on which programming language you use like as
      if you use PHP, you have to define the reroute page in .htaccess file.
      if you use nodejs with expressjs, you need to define in the router like as bellow

      /**
      **For localhist (for site panel)
      *@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 path=" + path.resolve(__dirname + "/../../dist/index.html"));
      res.sendFile(path.resolve(__dirname + "/../../dist/index.html"));
      });

      /**
      *For localhist/admin (for admin panel)
      *@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"));
      });

      Note : here
      ->api/ means all matching apis likes as http://localhost/api/user/getInfo
      ->css/ means all matching css likes as http://localhost/assets/css/style.css
      etc...

      This routing put at the end of routing instead of use * routing you should need to put like as above code as per your project's configuration.

      You may need to change the code as per your requirement.

      This is the method which I know but in nodejs maybe there are Lots of ways to send index.html of admin panel or site panel.

      Delete