Accurate reading of Environment Variables in Angular

Accurate reading of Environment Variables in Angular

In this post, we will see how we can Read Environment Specific Variables in Angular

This is a simple project which demonstrates reading information based on the environment. We have a header that changes based on the environment and there are four blocks representing each environment and turns green based on the environment.

Here is the example project where you can clone and run on your machine

<span id="4c9f" class="lg js ek at kl b gm lh li r lj">// clone the project
git clone [https://github.com/bbachi/angular-environment-info.git](https://github.com/bbachi/angular-environment-info.git)</span><span id="8b35" class="lg js ek at kl b gm lk ll lm ln lo li r lj">// install dependencies and run the project
npm install</span><span id="5d8e" class="lg js ek at kl b gm lk ll lm ln lo li r lj">ng serve --configuration=local   // local environment
ng serve --configuration=dev   // dev environment
ng serve --configuration=test   // test environment
ng serve --configuration=production   // production environment</span>
Configuration

Let’s dive into the configuration and go through all the steps to set up the project. First, we need to define four environment files since we have four environments local, dev, test, and prod.

Here are the four environment files. I put these in one file for the brevity.

// environment.ts
export const environment = {
  production: false,
  environment: 'Local'
}


// environment.dev.ts
export const environment = {
    production: false,
    environment: 'Development'
};

// environment.test.ts
export const environment = {
    production: false,
    environment: 'Test'
};


// environment.prod.ts
export const environment = {
    production: true,
    environment: 'Production'
};

Environment Files

Environment Files In the Project

Once you define all the environment files, We need to configure these in the angular.json file. This CLI configuration file contains build targets where you can specify specific information for the target builds and each build target has the section called fileReplacements which allows you to replace any file with a target-specific version of that file.

Here is the angular.json configuration file. I removed all the other info and kept only build and serve related information for the brevity. You can check the complete file here.

If you look at the configurations object under build object, you can see three environments defined called production, dev, and test. You can use these build targets for the serve section with the browserTarget option. For example, if you define test object under build configuration, you need to use angular-environment-info:build:test for the browserTarget

{
  "$schema": "./node_modules/@angular/cli/lib/config/schema.json",
  "version": 1,
  "newProjectRoot": "projects",
  "projects": {
    "angular-environment-info": {
      "root": "",
      "sourceRoot": "src",
      "projectType": "application",
      "prefix": "app",
      "schematics": {},
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "options": {},
          "configurations": {
            "production": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.prod.ts"
                }
              ],
            },
            "dev": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.dev.ts"
                }
              ]
            },
            "test": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.test.ts"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "angular-environment-info:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "angular-environment-info:build:production"
            },
            "dev": {
              "browserTarget": "angular-environment-info:build:dev"
            },
            "test": {
              "browserTarget": "angular-environment-info:build:test"
            }
          }
        }
}

angular.json

Once this configuration is done, all you need to import the base environment file wherever you need it. Remember, you need only import the base environment file Angular will take care of replacing these based on the configuration provided in the angular.json file.

Here is an example, I am using the environment information in the header section. All I need to do is import the base environment and use it like below. You can check the complete example here.

<div class="header">
  <h1>Welcome To {{env}} Profile</h1>
</div>
import { Component, OnInit } from '@angular/core';
import { environment } from '../../environments/environment';

@Component({
  selector: 'app-header',
  templateUrl: './header.component.html',
  styleUrls: ['./header.component.css']
})
export class HeaderComponent implements OnInit {

  constructor() { }
  env: string;

  ngOnInit() {
    this.env = environment.environment;
  }

}

header

Development with different environments

We are done with all the configuration part. Once you clone the above project. All you need to run ng serve with a configuration flag.

// development
ng serve --configuration=dev

// test
ng serve --configuration=test

// production not recommended (just for demonstration purpose)
ng serve --configuration=production

// local
ng serve
Build for productions with different environments

Building projects are very similar to running them in the development mode. But, when you build the project with the ng build with the configuration flag all the code is compiled and put it inside /dist folder.

// development
ng build --configuration=dev

// test
ng build --configuration=test

// production
ng build --configuration=production

// local
ng build

You can just load index.html page under /dist/. You should change the paths of scripts generated in the index.html since we are not running any nginx or node server to serve these pages.

build

change the paths to load properly

Summary
  • Every project has environment-specific information which can be loaded based on the environment in which we are running the application.
  • Angular provides an opinionated way where we can define these environments and angular loads automatically based on the configuration provided in the angular.json
  • We just need to import the base configuration file, Angular automatically replaces that with the file mentioned in the fileReplacements section of the angular.json.
  • We can run the application in the development mode with the ng serve and we can pass environment-specific information with the configuration flag.
  • We can build the application with the ng buildand we can pass build targets with the configuration flag.
Conclusion

In this way, we can provide environment-specific information based on the environment in which we are running the apps. Thanks and love all

Learn Angular from Scratch and Go from Beginner to Advanced

Learn Angular from Scratch and Go from Beginner to Advanced

In this Angular Tutorial, you'll learn Angular from scratch and go from beginner to advanced in Angular. In this Angular crash course you will learn from scratch. We will assume that you are a complete beginner and by the end of the course you will be at advanced level. This course contain Real-World examples and Hands On practicals.

Complete Angular Course: Go From Zero To Hero

Complete Angular course. Learn Angular from scratch and go from beginner to advanced in Angular.

Welcome to this course "Complete Angular Crash Course: Learn Angular from Scratch and Go from Beginner to Advanced". In this course you will learn from scratch. We will assume that you are a complete beginner and by the end of the course you will be at advanced level. This course contain Real-World examples and Hands On practicals. We will guide you step by step so that you can understand better. This course will allow you to work on the Real-World as a professional.

What you'll learn

  • Learn The Basics
  • Learn Advanced Methods
  • Step By Step Instructions So That You Can Go From Zero To Hero
  • A Complete Tutorial Explaining Everything You Need To Know
  • Real-World Examples With Hands On Tutorials
  • Get Answers To Your Every Single Questions

JavaScript Tutorial: if-else Statement in JavaScript

JavaScript Tutorial: if-else Statement in JavaScript

This JavaScript tutorial is a step by step guide on JavaScript If Else Statements. Learn how to use If Else in javascript and also JavaScript If Else Statements. if-else Statement in JavaScript. JavaScript's conditional statements: if; if-else; nested-if; if-else-if. These statements allow you to control the flow of your program's execution based upon conditions known only during run time.

Decision Making in programming is similar to decision making in real life. In programming also we face some situations where we want a certain block of code to be executed when some condition is fulfilled.
A programming language uses control statements to control the flow of execution of the program based on certain conditions. These are used to cause the flow of execution to advance and branch based on changes to the state of a program.

JavaScript’s conditional statements:

  • if
  • if-else
  • nested-if
  • if-else-if

These statements allow you to control the flow of your program’s execution based upon conditions known only during run time.

  • if: if statement is the most simple decision making statement. It is used to decide whether a certain statement or block of statements will be executed or not i.e if a certain condition is true then a block of statement is executed otherwise not.
    Syntax:
if(condition) 
{
   // Statements to execute if
   // condition is true
}

Here, condition after evaluation will be either true or false. if statement accepts boolean values – if the value is true then it will execute the block of statements under it.
If we do not provide the curly braces ‘{‘ and ‘}’ after if( condition ) then by default if statement will consider the immediate one statement to be inside its block. For example,

if(condition)
   statement1;
   statement2;

// Here if the condition is true, if block 
// will consider only statement1 to be inside 
// its block.

Flow chart:

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If statement 

var i = 10; 

if (i > 15) 
document.write("10 is less than 15"); 

// This statement will be executed 
// as if considers one statement by default 
document.write("I am Not in if"); 

< /script> 

Output:

I am Not in if
  • if-else: The if statement alone tells us that if a condition is true it will execute a block of statements and if the condition is false it won’t. But what if we want to do something else if the condition is false. Here comes the else statement. We can use the else statement with if statement to execute a block of code when the condition is false.
    Syntax:
if (condition)
{
    // Executes this block if
    // condition is true
}
else
{
    // Executes this block if
    // condition is false
}


Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate If-else statement 

var i = 10; 

if (i < 15) 
document.write("10 is less than 15"); 
else
document.write("I am Not in if"); 

< /script> 

Output:

i is smaller than 15
  • nested-if A nested if is an if statement that is the target of another if or else. Nested if statements means an if statement inside an if statement. Yes, JavaScript allows us to nest if statements within if statements. i.e, we can place an if statement inside another if statement.
    Syntax:
if (condition1) 
{
   // Executes when condition1 is true
   if (condition2) 
   {
      // Executes when condition2 is true
   }
}

Example:

<script type = "text/javaScript"> 

// JavaScript program to illustrate nested-if statement 

var i = 10; 

if (i == 10) { 

// First if statement 
if (i < 15) 
	document.write("i is smaller than 15"); 

// Nested - if statement 
// Will only be executed if statement above 
// it is true 
if (i < 12) 
	document.write("i is smaller than 12 too"); 
else
	document.write("i is greater than 15"); 
} 
< /script> 

Output:

i is smaller than 15
i is smaller than 12 too
  • if-else-if ladder Here, a user can decide among multiple options.The if statements are executed from the top down. As soon as one of the conditions controlling the if is true, the statement associated with that if is executed, and the rest of the ladder is bypassed. If none of the conditions is true, then the final else statement will be executed.
if (condition)
    statement;
else if (condition)
    statement;
.
.
else
    statement;


Example:

<script type = "text/javaScript"> 
// JavaScript program to illustrate nested-if statement 

var i = 20; 

if (i == 10) 
document.wrte("i is 10"); 
else if (i == 15) 
document.wrte("i is 15"); 
else if (i == 20) 
document.wrte("i is 20"); 
else
document.wrte("i is not present"); 
< /script> 

Output:

i is 20

How To Deploy Micro-Frontend Architecture with Angular

How To Deploy Micro-Frontend Architecture with Angular

In this post, we discuss how to Implement Micro-Frontend Architecture With Angular

Introduction

Micro-frontends are small applications mostly divided by subdomain or functionality working together to deliver a larger application. Before diving into Mirco-frontends, we will understand what are micro frontends and why we are talking about those.

Usually, projects come in different sizes and different requirements. If your project is simple enough to have two or three pages and can be maintained by a single team, you don’t have to think about these Micro-frontends. You can just implement with any of your chosen framework such as Angular, React, or Vuejs.

But, this is not the case all the time. Sometimes your frontend app might be a small part of another big application or your app consists a lot of sections and features which are developed by different teams or your app is being released into production feature by feature with developed by separate teams. If you are in one of these situations then you need to think about Micro Frontends. Let’s look at it in the below diagram.

Micro Frontends Architecture

As you see in the above diagram, we have 6 frontend apps working together to deliver the large application. The communication between these apps can be done with an event bus, window object, or publish/subscribe methods. Each app can be implemented by a separate team and any framework. Each app can talk to their backends or endpoints individually. There is a bootstrap/launch app that loads all the apps and mounts and unmounts in the DOM depending on the user interaction or routing.

A Journey Into Mirco-Frontends

We will first understand why do we need this in the first place. Let’s have a look at the present technologies that we use for user faced web applications.

Advantages of Mirco-Frontends

Here are the advantages of this architecture.

Apps are small

Obviously, apps become small when we split the large application by sections, pages or features.

Apps are independent

Since all the apps are divided and developed separately these are independent of each other

Apps are easier to understand

Apps are easier to understand because they are small and developed by a single team.

Apps are easier to develop and deploy

As these apps are small in nature and developed by a single team its very easy to develop and deploy. We can even deploy independently.

Apps are easier to test

We have to write thousands and thousands of unit tests for larger applications and takes forever to run. This makes out deployment process slower. When it comes to micro frontends each app has few unit tests and executes its own unit tests and can be run independently.

Apps development becomes faster

The whole development becomes faster and easier because of separate teams.

CI/CD Becomes easier

Each app can be integrated and deployed separately and this makes the CI/CD process a lot easier. When we fix the app or introduce a new feature we don’t have to worry about the entire application since all the features are independent.

Independent Stacks and versions

We can choose our own stack for each app but this doesn’t happen so often But, we can have different versions of the same stack. For example, Some teams have the flexibility and time to introduce and test newer versions of the same stack.

No Shared Code

In large applications, we tend to share code across features but, this doesn’t scale well and introduce a lot of bugs and interdependency as the app grows bigger and bigger. This doesn’t apply with the Micro-frontends as we do not share the code unless it is a dumb component.

Can change architecture easily without touching old one

Sometimes we have to extend the old architecture but we might not have the developers to implement or extend the architecture. With the Micro frontends approach, we can develop the new feature with the latest stack and deliver independently.

Features of Mirco-Frontends
  • Each frontend represents a specific feature or subdomain of the entire application
  • Each frontend can be implemented with a separate team.
  • Each frontend can be implemented with different technology.
  • They cannot share logic and its independent of each other.
  • Each Frontend can be owned by a single team.
How Do We Split Apps

Let’s see how we can split large applications into micro frontends. There are no specific criteria to divide the apps and we can separate in a number of ways depending on our needs. We will see all the possible ways that we could divide apps.

By Feature

This is the most common method since we can easily divide the features of the app. For example, if there are three features for the app Dashboard, Profile and views we can make each feature as a separate app and mounts and unmounts in the DOM with the help of Launch.js. This Launch.js can be a separate app or just a simple javascript app.

By Section

Some of the apps have so much functionality with each section, for example, coinbase, Gmail, etc. We can implement each section as a new app in that scenario.

By Page

Some app’s functionalities are divided by page. Each page has some functionality that can be independent. We can divide these apps by page We have four pages in the below diagram. we can make four apps out of this.

By Domain

Splitting app based on the domain is also one of the most common approaches.

Micro-Frontend Frameworks

Micro frontends have been implemented for at least two years and it is still a green field. Have you ever wonder are there any frameworks or libraries to implement these and makes our job easier. The answer is yes and there are a couple of libraries or frameworks

single-spa

single-spa is a javascript framework for front-end microservices and can be implemented with all three popular frameworks/libraries such as Angular, React and Vue.js. It can lazy load the applications based on the need and You can check out their website for more information.

frint.js

frint.js is a Modular JavaScript framework for building Scalable & Reactive applications. It doesn’t support Angular yet but it supports React. If you are building a reactive application from scratch and you are just starting, this is the framework for you. You can check out their website for more information.

Example Micro-frontend Project With Angular

With all this information let’s build an example Angular project with the help of a single-spa framework. I would like to build a simple app for the demonstration and I will do the full example with all the features in another post.

We will split this app by section as shown in the below diagram. We are going to implement 4 apps in total: HeaderApp, DashboardApp, FooterApp, and root application.

Here are the four repositories for four apps. You can clone these run separately on your machine.

// root app runs on port 4200
git clone https://github.com/bbachi/micro-root.git
npm install
npm start

// micro header runs on port 4300
git clone https://github.com/bbachi/micro-header.git
npm install
npm start

// micro dashboard runs on port 4202
git clone https://github.com/bbachi/micro-dashboard.git
npm install
npm start

// micro footer runs on port 4201
git clone https://github.com/bbachi/micro-footer.git
npm install
npm start

You can access the whole application on http://localhost:4200/

Running all four apps together to make it one app

Here is the micro-root app index HTML file. We are importing all the three apps on line 10 and registering the apps with the appropriate name and location. Since we are loading all the apps on page load, we are not defining any specific context paths.

<!DOCTYPE html>
<html>
  <head>
    <meta http-equiv="Content-Security-Policy" content="default-src *  data: blob: 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'; connect-src * 'unsafe-inline'; img-src * data: blob: 'unsafe-inline'; frame-src *; style-src * data: blob: 'unsafe-inline'; font-src * data: blob: 'unsafe-inline';">
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Your application</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <meta name="importmap-type" content="systemjs-importmap">
    <script type="systemjs-importmap">
      {
        "imports": {
          "footer": "http://localhost:4201/main.js",
          "dashboard": "http://localhost:4202/main.js",
          "header": "http://localhost:4300/main.js",
          "single-spa": "https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.5/system/single-spa.min.js"
        }
      }
    </script>
    <link rel="preload" href="https://cdnjs.cloudflare.com/ajax/libs/single-spa/4.3.5/system/single-spa.min.js" as="script" crossorigin="anonymous" />
    <script src='https://unpkg.com/[email protected]/minified.js'></script>
    <script src="https://unpkg.com/zone.js"></script>
    <script src="https://unpkg.com/[email protected]/dist/import-map-overrides.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/system.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/extras/amd.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/extras/named-exports.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/systemjs/4.0.0/extras/named-register.min.js"></script>
    <style>
    </style>
  </head>
  <body>
    <script>
      System.import('single-spa').then(function (singleSpa) {
        singleSpa.registerApplication(
          'header',
          function () {
            return System.import('header');
          },
          function (location) {
            return true;
          }
        )

        singleSpa.registerApplication(
          'dashboard',
          function () {
            return System.import('dashboard');
          },
          function (location) {
            // return location.pathname.startsWith('/app2');
            return true;
          }
        )

        singleSpa.registerApplication(
          'footer',
          function () {
            return System.import('footer');
          },
          function (location) {
            // return location.pathname.startsWith('/app1');
            return true;
          }
        );
        
        singleSpa.start();
      })
    </script>
    <import-map-overrides-full></import-map-overrides-full>
  </body>
</html>

root app index.html

You can give /header location path so that the header is loaded when browser URL navigates to /header. Let’s test it out.

<script>
      System.import('single-spa').then(function (singleSpa) {
        singleSpa.registerApplication(
          'header',
          function () {
            return System.import('header');
          },
          function (location) {
            return location.pathname.startsWith('/header');
            // return true;
          }
        )

with /header

Header only load with the route /header

Conclusion

I know Microfrontends is such a trendy thing but you should not use it for every app. Do not use it if your app is small and don’t complicate it. This approach should make our whole process seamless not over complicated. So use your best judgment before using this approach.

Thank for reading and keep vistting!