Building multiple versions of the same applications with a single project

Building multiple versions of the same applications with a single project

I will start with a little context on why we had to do this. So, the long term plan of Ubilog (NEFTAG cloud) is that it will be a central hub for various loggers, not just NEFTAG. For example, we will have neftag.ubilog.io, intellilog.ubilog.io etc.. Of course, to give the end user a consistent experience from App to cloud, each of the subdomains has to be branded accordingly.

Since we started with NEFTAG first, we had the name NEFTAG in various places, and it had the NEFTAG logo and color scheme. We had to find a way to make the name, logo and color scheme dynamic without making a lot of changes.

Challenge 1: Making the application name dynamic

The first challenge was to replace the application name with a global variable.

I decided to use environment files for this. One environment file each for NEFTAG and Intellilog.

export const environment = {
    production: true,
    APPLICATION_NAME: "NEFTAG",
    SUPPORT_EMAIL: "support@neftag.com",
    MAIN_COLOR: "#007fa1"
};
export const environment = {
    production: true,
    APPLICATION_NAME: "Intellilog",
    SUPPORT_EMAIL: "support@intellilog.com",
    MAIN_COLOR: "#0D6da0"
};

Now, wherever the name of the app is being used, import environment and use the variable environment.APPLICATION_NAME

Challenge 2: Making the application logo dynamic

Well this one was easy. I was already using svg logo and had a component for that, which was being re-used wherever the logo was needed. All I needed to do was import the enviroment variable from above and check which application was being used and used ngIf in the html template. Something like this:

<svg *ngIf="environment.APPLICATION_NAME == 'NEFTAG'"/>
    
<svg *ngIf="environment.APPLICATION_NAME == 'Intellilog'"/>

And in the ts file:

import { environment } from '../../../../environments/environment';

public environment = environment;

*I did not paste the entire logo code here. This is just to demonstrate how to use ngIF in svg.

Challenge 3: Making the color scheme dynamic

We have different color schemes for NEFTAG and Intellilog. The NEFTAG primary color is #007fa1 and Intellilog primary color is #0D6da0. For the svg logos, we imported the color from the environment variable. But that does not work for the overall color scheme using CSS.

So I decided to use less files instead of CSS. Less is a superset of CSS similar to how typescript is a superset of javascript. In less files, we can set variables and reuse them by importing them into other less files. So, all I needed to do was to use two sets of less files, with the primary color variable having different values as per the project.

Challenge 4: Deploying without any changes

Now, I wanted to deploy the application in production without fiddling with the config files, changing the environment variable values, etc. I followed the following steps:

  1. Created two index.html files. One for NEFTAG and one for Intelliglog and named them index_neftag.html and index_intellilog.html. (I had to do this because we had logos on index.html file and these were hardcoded into the HTML as the logo component cannot be used before bootstrapping the application.)
  2. Created two favicon.ico files and named them favicon_neftag.ico and favicon_intellilog.ico.
  3. Now comes the main part; the .angular-cli.json file. It has an option to declare multiple apps and give them names. Have a look:
{
    "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
    "project": {
        "version": "1.3.2",
        "name": "neftag-cloud-frontend"
    },
    "apps": [{
        "name":"neftag",
        "root": "src",
        "outDir": "dist",
        "assets": [
            "assets",
            "favicon_neftag.ico"
        ],
        "index": "index_neftag.html",
        "main": "main.ts",
        "polyfills": "polyfills.ts",
        "test": "test.ts",
        "tsconfig": "tsconfig.app.json",
        "prefix": "app",
        "styles": [
            "../node_modules/bootstrap/dist/css/bootstrap.min.css",
            "../node_modules/font-awesome/css/font-awesome.min.css",
            "../node_modules/@swimlane/ngx-datatable/release/themes/material.css",
            "../node_modules/ng2-slim-loading-bar/style.css",
            "assets/less/neftag/core.less",
            "assets/less/neftag/components.less",
            "assets/less/neftag/pages.less",
            "assets/less/neftag/menu.less",
            "assets/less/neftag/responsive.less",
            "styles.css"
        ],
        "scripts": [
            "../node_modules/jquery/dist/jquery.min.js",
            "../node_modules/bootstrap/dist/js/bootstrap.min.js",
            "assets/js/waves.js",
            "../node_modules/file-saver/FileSaver.js",
            "../node_modules/hidpi-canvas/dist/hidpi-canvas.min.js"
        ],
        "environmentSource": "environments/environment.ts",
        "environments": {
            "dev": "environments/environment.ts",
            "prod": "environments/environment.neftag.prod.ts"
        }
    },
    {
        "name":"intellilog",
        "root": "src",
        "outDir": "dist",
        "assets": [
            "assets",
            "favicon_intellilog.ico"
        ],
        "index": "index_intellilog.html",
        "main": "main.ts",
        "polyfills": "polyfills.ts",
        "test": "test.ts",
        "tsconfig": "tsconfig.app.json",
        "prefix": "app",
        "styles": [
            "../node_modules/bootstrap/dist/css/bootstrap.min.css",
            "../node_modules/font-awesome/css/font-awesome.min.css",
            "../node_modules/@swimlane/ngx-datatable/release/themes/material.css",
            "../node_modules/ng2-slim-loading-bar/style.css",
            "assets/less/intellilog/core.less",
            "assets/less/intellilog/components.less",
            "assets/less/intellilog/pages.less",
            "assets/less/intellilog/menu.less",
            "assets/less/intellilog/responsive.less",
            "styles.css"
        ],
        "scripts": [
            "../node_modules/jquery/dist/jquery.min.js",
            "../node_modules/bootstrap/dist/js/bootstrap.min.js",
            "assets/js/waves.js",
            "../node_modules/file-saver/FileSaver.js",
            "../node_modules/hidpi-canvas/dist/hidpi-canvas.min.js"
        ],
        "environmentSource": "environments/environment.ts",
        "environments": {
            "dev": "environments/environment.ts",
            "prod": "environments/environment.intellilog.prod.ts"
        }
    }],
    "addons": [],
    "packages": [],
    "e2e": {
        "protractor": {
            "config": "./protractor.conf.js"
        }
    },
    "lint": [
      {
          "project": "src/tsconfig.app.json"
      },
      {
          "project": "e2e/tsconfig.e2e.json"
      }
    ],
    "test": {
        "karma": {
            "config": "./karma.conf.js"
        }
    },
    "defaults": {
        "styleExt": "css",
        "component": {
            "inlineTemplate": false,
            "spec": true
        }
    }
}

So, as you can see, I have two apps configured in the .angular-cli.json file.

  1. In order to build the application for production, all I have to do is this:

ng build --prod --app neftag
ng build --prod --app intellilog

This way I can get production files for NEFTAG and Intellilog, without making any changes. I have included a screenshot of the cloud for NEFTAG and Intellilog below.

Screen-Shot-2017-09-05-at-2.22.06-PM-2-2