Angular Authentication with AWS Cognito and Amplify

Angular Authentication with  AWS Cognito and Amplify

Angular Authentication with AWS Cognito and Amplify. You have been able to successfully create a fully-functioning user authentication using AWS Amplify and AWS Cognito with Angular

In this article, we will be looking at implementing a serverlesssolution with the AWS Amplify library with Angular8 applications.

We will be creating a fully-functioning user authentication that includes user signup, verifying the email, sign in, and sign out. To make all these things happen we will be using AWS Cognito, a handy service provided by AWS.

To get started with that, an AWS account is required and if you don’t have an account, you can create a free account at AWS. If you already have an account, you are good to go.

TL;DR

What Is Amplify?

Amplify is an open-source library that helps you to easily build a flexible, reliable, and scalable serverless back end and integrate it with your front end. This was first launched in November at AWS re:Invent 2017.

“An opinionated, category-based client framework for building scalable mobile and web apps” — AWS

The Amplify framework consists of three key features:

  • Libraries: Come with a set of comprehensive libraries for JavaScript, Android, and iOS associated with cloud services, such as analytics, authentication, storage, notifications, REST, and GraphQL APIs, AR/VR.
  • Amplify CLI: Command-line tool for configuring and managing serverless infrastructures on AWS.
  • UI components: Set of framework-specific cool UI components for React, React Native, Angular, Ionic, and Vue for authentication, photo picker, etc. to make your developments easy.

These UI components come with super handy themes but in this tutorial, we will develop our own UI components.

Let’s Start

To get started, first, we need to install and configure the AWS Amplify CLI on our local machine. We will use npm to install the CLI and this will require Node.js and npm installed on your machine

Make sure your Node version is at least 8.x.x or higher and your npm version is 5.x.x or higher. If not, you need to upgrade your Node and npm to meet at least the minimum requirements.

To install the Amplify CLI in your terminal, simply run the following command, this will globally install the CLI on your local machine:

npm install -g @aws-amplify/cli

Once you’ve installed the CLI, you need to configure it by creating an IAM user to create and manage your AWS resources. To do that, simply run:

amplify configure

This process will take you through several steps.

  • This will open up the AWS console in your browser and you need to log in to your account and once you are done, you need to press enter in the terminal to continue the process.
  • Next, it will ask you to specify the AWS region: us-east-1 (I’m selecting us-east-1, North Virginia as my region, this may vary according to your region).
  • Next, it will ask you to specify the username of the new IAM user: angular-amplify-demo (you may use any name according to your preference)
  • As soon as you hit the enter, it will redirect you to the IAM management console. You can click next to complete creating the user and we will leave the default settings and permissions. You can head over to the terminal and hit enter to continue.

Next, it will ask:

  • accessKeyId: YOUR_ACCESS_KEY_ID
  • secretAccessKey: YOUR_SECRET_ACCESS_KEY

(You can grab these keys from your IAM user.)

Next, it will ask for a profile name: (default) and I’m leaving this as default, but you can give a profile name for this.

This will create/update the AWS profile on your local machine that is associated with the above accessKeyId and secretAccessKey. With this step, you have successfully set up the new user.

Alternatively, you can watch a video tutorial about the configuration process.

Next step…

Congratulations!

With this, you have now successfully installed and configured AWS Amplify on your local machine.

Creating the Angular Project

Next, we need to create our Angular project. I will be using the Angular CLI to generate a new Angular app and for that, you need to have the Angular CLI installed on your machine.

If you don’t, simply run the below command to install the Angular CLI globally:

npm install -g @angular/cli

To create and run a new app, simply run:

ng new aws-amplify-cognito-authentication
cd aws-amplify-cognito-authentication
ng serve

This will start your application and you can navigate to localhost:4200 from yous browser to open the application.

Install the Amplify Libraries

To get started with Amplify, we need to install the aws-amplify and aws-amplify-angular libraries as dependencies of our Angular application.

The aws-amplify-angular package is a set of Angular components and an Angular provider that helps integrate your application with the AWS Amplify library. It supports Angular 5.0 or above.

Configure the Back End

Now, we need to configure our back end for the project and enable authentication with Amazon Cognito user pools. To initialize the back-end project, simply run:

amplify init

And this will take you through a series of questions that are required to set up our back end.

  • Choose your default editor: Visual Studio Code. (You can choose the code editor that you are using.)
  • Choose the type of app that you’re using: JavaScript.
  • Which JavaScript framework are you using: Angular.
  • Source directory path: src.(You need to specify the source directory path of your project, I will accept the default by hitting the enter as src is our source directory.)
  • Destination directory path: dist/aws-amplify-cognito-authentication. (By default, Angular will create a subfolder with the same name, therefore, make sure to specify the subfolder as the destination directory path.)
  • Build command: npm run-script build
  • Start command: ng serve
  • Do you want to use an AWS profile? Yes.
  • Please choose the profile you want to use: default.(If you gave a name to your profile during the Amplify configuration process, make sure to select that name as your profile.)

And with that, it will take some time to initialize the project in the cloud and once done, you will see a successful message in your terminal with a bunch of other information.

Now we have successfully configured our back end.

Then, we need to configure AWS Cognito by creating an Amazon Cognito user pool, which is a full-featured user-directory service that helps us to handle our user registration and authentication.

To start with that, simply run the command:

amplify auth add

And again, this will take you through a series of questions that are required to set up our user pools.

  • Do you want to use the default authentication and security configuration: Yes, use the default configuration.

But I will choose the default configuration so that it immediately configures Cognito locally. Now we need to publish our configurations to the cloud so we can start using the service.

To do that, simply run:

amplify publish

This will show you a summary of your configuration in a tabular view and you can confirm by saying: “Yes”.

This process will take some time to create our Cognito user pool and Congito identity pool and once done, it will show a success message in your terminal.

You can visit your AWS console to check if the user pool has successfully been created (make sure you have selected the correct region).

What’s Next?

Now that we are all set with our back end, we need to configure our front-end application for the authentication process.

Now, if you have noticed, a new file called aws-exports.jshas been created inside your Angular application, which holds all the configuration related to AWS Cognito.

As the next step, we need to import these configurations to our app. In your main.ts file, you need to add the following imports:

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import Amplify from 'aws-amplify';
import awsconfig from './aws-exports'; 

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

Amplify.configure(awsconfig);

if (environment.production) {
  enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
  .catch(err => console.error(err));

main.ts

With Angular 6+, the Angular team decided to remove the support for “global” and “process” variables in the browser, therefore, all the Angular 6+ applications don’t include shims for that.

To add the support, you need to add the following code in your polyfills.ts file:

(window as any).global = window;
(window as any).process = {
  env: { DEBUG: undefined },
};

polyfills.ts

Next, we need to include the “node” package that should be included in the types compiler option in your src/tsconfig.app.json file to work with aws-js-sdk.

"compilerOptions": {
    "types" : ["node"]
}

All set to go, let’s register our first user in the AWS Cognito pool.

Sign Up

To send our user object to Cognito, your need to import the aws-amplify library to your component.

import { Auth } from 'aws-amplify';

To create a new user in your user pool:

 Auth.signUp(user)
      .then(data => {
        console.log(data);
        this.toVerifyEmail = true;
        this.signstatus = '';
      })
      .catch(err => console.log(err));

user-authentication.component.ts

The Auth.signUp() method takes the user object as an argument with required fields for user registration, which looks like this, in our case:

const user = {
   username,
   password,
   attributes: {
        email,
        phone_number
        // other custom attributes
      }
}

Now, when you pass the username, you have to ensure that you have set the username as the sign-in method in your Cognito user pool.

If you have selected email as your sign-in method, you need to send the user email as the username property in the user object.

You can check which attribute you have set as the sign-in method in the Cognito user pool by visiting the Attributes tab under the General settings in your Cognito user pool.

In this example, I have set the Email address or phone number as my sign-in option.

This Auth.signUp() promise returns a data object which is a type of CognitoUser and if you successfully sign up, you will get a six-digit verification code sent to your email to verify your email address.

Verify the Email

To verify your email address:

 Auth.confirmSignUp(userName, verifycode, 
  {forceAliasCreation: true}).then(data => {
        console.log(data)
        this.toVerifyEmail = false;
        this.signstatus = 'signin'
     })
       .catch(err => console.log(err));

user-authentication.component.ts

The Auth.confirmSignUp(userName, verifyCode) promise takes two arguments:

  • userName: User name or email address that we have chosen to sign in the user. (In our case, it’s email address.)
  • verifyCode: Six-digit verification code that was sent to your email address.
Sign In

To sign in your newly created user:

Auth.signIn(user).then(user => {
      console.log(user);
    })
      .catch(err => console.log(err));

user-authentication.component.ts

The Auth.signIn(user) promise takes one argument that includes the username and password:

const_ user = {
      username,
      password
   }

And that returns the user object which will contain the challengeName and challengeParam properties that you can use to check if the user has enabled two-factor authentication, etc.

Sign Out

To sign out the user from the current session:

Auth.signOut()
    .then(data => console.log(data))
    .catch(err => console.log(err));

dashboard.component.ts

This will clear the current user session by revoking the auth token.

If you want to sign out the user from all the devices you need to pass an additional configuration to your sign-out method, like this:

Auth.signOut({ global: true })
    .then(data => console.log(data))
    .catch(err => console.log(err));

This will ensure that your user is signed out from all the devices if the user has been logged in from multiple devices.

Conclusion

Congratulations, you have been able to successfully create a fully-functioning user authentication using AWS Amplify and AWS Cognito with Angular, by:

  • Installing the AWS Amplify CLI on our local machine and configuring it with our AWS account.
  • Configuring the serverless back end with the help of the Amplify CLI and AWS Cognito cloud service for user authentication.
  • Creating an Angular 8 project using the Angular CLI.
  • Using aws-amplify libraries to sign up, confirm password, sign in, and sign out the user.

Although this article is a bit lengthy, we have still only covered the very basic features that are provided by aws-amplify library.

You can explore more features such as change password, forget password, verifying the phone number, etc. that are related to user registration by referring to the official Amplify documentation.

Additionally, here’s the complete GitHub project.

Thanks for reading!

Node vs Angular : Comparing Two Strong JavaScript Technologies

Just from being a simple client-side scripting language, JavaScript has evolved over the years to turn out to be a powerful programming language. Here Node.js is a cross-platform runtime environment while AngularJS is one of the top JavaScript framework. Angular helps the developers to build web applications which are dynamic in nature using HTML template language and following the MVC design pattern.

15 Common Mistakes Developers Made While writing JavaScript Apps

15 Common Mistakes Developers Made While writing JavaScript Apps

Every developer make mistake some time when writing apps. This is a normal part of the development process. In this post, we'll learn 15 Common Mistakes Developers Made While Writing JavaScript Apps hope this post will help you

Common Mistakes Developers Made While Writing JavaScript Apps

Every developer make mistake some time when writing apps. This is a normal part of the development process. There are some mistakes that are made more often than others. It is good to be aware of them so that they can be corrected before it’s made. Once we can do that then we can develop apps a lot easier and become a proficient JavaScript developer since we can develop apps that makes less bugs. We will go through some of the more common bugs that are made so we can avoid them here

1. Confusing the Equals

In JavaScript, single equal, double equal, and tripe equal all have their own meanings. Single equal sign is the assignment operator which assigns the value on the left to the variable to the right. Double equal sign is the equality check operator which checks an object’s content for equality without checking its type. The triple equals operator checks an object’s content and the type for equality.

We use the single equal assignment operator like the following:

let a = 1

This is used for setting variable values, so it’s we use single equal in a place that shouldn’t be used like in:

if (a = 1){ ... }

then we would be assigning 1 to a which is always true . This means that the code inside the if statement always runs. That’s definitely not what we want.

Double equal sign shouldn’t be too too much since it doesn’t check the type of the object when checking for equality, so something like:

1 == '1'

if true . This creates problems because if we are only checking for number 1 and we get string with 1 in it, it’s still true. We want to convert them to the same type and then compare them with === to check for equality to avoid ambiguity. So if we want to compare if all forms of 1 are equal to each other we convert them to number first by using:

Number(1) === Number('1')

1 wouldn’t be equal to '1' is we use === since they are different types. This would make things a lot more clear. If we want to make sure something aren’t equal to each other then we can use !== to check if they don’t have the same content or the same type, so:

1 !== '1'

would be true .

2. Mismatching Brackets

Nested statements and functions in each other means that there’re multiple levels of brackets in each file. Usually, apps are pretty complex so the levels can add up. This means that mismatching brackets is easy if you use a text editor that doesn’t support syntax highlight or check for mismatched brackets. This can easily be avoided with modern text editors such as Visual Studio Code, Atom, and Sublime. If we want to use simpler text editors, then use linters and code formatting tools like ESLint and Prettier to detect these issues. They also let us format code automatically and detect common style issues that may occur like quote style inconsistencies, number of characters in a line, functions that can be shortened, etc.

3. Mismatching Quotes

JavaScript lets us use single quotes, double quotes, and backticks for strings. They are equivalent. However, we should open and close with the same character. So if we open a string with single quote then use single quote to close a string, and if we started with double quotes or backticks, then use those to close the string respectively. Also, special characters like quotation marks have to escape to be included in a string in some cases. If you open a string with single quotes, then if you use single quotes in a string, then you have to escape that to include it in the string. This also applies to double quotes and backticks. If you use double quotes in a double quoted string then you have to escape it. And if you use a backtick in a template string, then you have to escape the backtick character.

4. Parentheses

In if statements, parentheses always have to wrap around the whole condition. For example, something like:

if (x > y) && (y < 10) {...}

won’t work. The correct way to write that statement is to write is:

if ((x > y) && (y < 10)) {...}

if we want to check that both conditions are true.

5. Semicolon

Semicolons are optional at the end of a JavaScript line. However, to make things clear for other people, we should include it so that people know where a line ends. We can use a code linter or formatter like ESLint or Prettier to do this automatically. Lots of text editors also have add-ons to fix it automatically for you.

6. Capitalization

JavaScript code is case-sensitive, so things that have different capitalization will be considered different even if they have the same spelling. Anything that references them also have to be capitalized correctly. For example, getElementsByTagName should always be spelled this way with exact same capitalization. The general convention in JavaScript is that we spell variables and function names with camel case, so we should stick with that for consistency if we define our own variables.

7. Referencing Code That’s not Yet Loaded

It’s important that we reference code that have been defined and loaded before hand in order to use the code. If we are using script tag then before we reference the code, we have to load the code first. So the script tag that references the script with the variable we want to use should come before the script tag that references the script that has the code that we want to reference.

Fortunately, JavaScript modules are much more common nowadays, so that we just have to remember to export the code that you want to use in another module and the import the exported variable in the module that you want to use the variable before referencing it. JavaScript modules solves a lot problems with script tags. Global variables do not have to used much anymore to let different scripts reference the same variable, and also we can make variables private without the use of closures since we only need to export the ones that are needed elsewhere.

8. Variable Names

In JavaScript, there are many reserved words that cannot be used as variable names. They shouldn’t be used as variable names since they collide the name of the reserved words, creating confusion with the JavaScript interpreter. To avoid this we should use names that are descriptive like firstName , lastName , and things like that.

9. Scope

We shouldn’t use global variables whenever possible. This means that all variables we declare should use the let or const keywords for variables and constants respectively. This avoids issues with global variable scope and overwriting global variable data. It also let us avoid errors from assigning to entities that should be constant or assigning things accidentally that are global.

10. Missing or Unexpected Parameters

When functions are called with expected parameters missing, then unexpected results may be returned because you passed in unexpected input to a function.We have to make sure that we pass in the right parameters to a function so that we get the right results. It also a good idea to check for unexpected parameters if the function is used in lots of places like functions that are in libraries so that we can deal with unexpected parameters gracefully like ending the execution of the function when parameters passed in are of an invalid type or null or undefined .

11. Index Errors

JavaScript arrays starts with index 0, so the last index of an array will always be 1 less than the length of the array. When you pass in an index that is beyond the length or it’s invalid like a negative number, we get undefined , so we will get ReferenceErrors if we try to get the invalid entry and do stuff with it.

12. Null and Undefined

null and undefined are things that are often the values of variables. This means that we have to check for these when we are getting something by traversing the nested properties of objects or looping through an array. When we have null or undefined we want to handle them gracefully. This means that we should always check for them. So we if have an object called foo and we want to access deeply nested properties within it, then we should check for null or undefined in each level, like so:

if (foo && foo.bar && foo.bar.baz){ ... }

This way we know that all the properties are defined so that we can get access the baz property. For arrays if we want to access index i , we should check if it’s defined by writing:

if (arr[i]){...}

so that we won’t get unexpected undefined errors.

The optional chaining operator (?. ) is being worked on, so hopefully it will be finalized soon and we can use it to access deeply nested properties in an object and we don’t have to deal with properties being undefined as much anymore. Also, Lodash has a get function, which can try to access deeply properties of arrays and objects and get the value if it exists or return null if there’s anything undefined in the middle of the object’s hierarchy. This is much better than a long chain of undefined or null checks.

13. Confusing Undefined and Null

JavaScript has both undefined and null for non-values. However, there are quite a few differences between the two. undefined means that the variable may have been declared, but nothing is set to it. A variable can also be explicitly set as undefined . The type of an undefined variable, when checking the type with the typeof operator, will get us the type 'undefined'. Functions that don’t return anything returns undefined. On the other hand, null values have to be explicitly set by functions that return null or just set directly to a variable. When we check an object that has the null value set, we’ll find that the type of it is'object' if a variable has the null value.

For this reason, it’s probably easier to stick to undefined whenever we can when we’re setting variable values to non-values. It reduces confusion and we only have to check that the type of a variable is 'undefined' to see whether it’s undefined. That’s less painful than having two checks, for both null and undefined.

To write functions that return undefined, we don’t have to do anything like the following example:

const f = () => {}

To set a variable that was assigned some other value to undefined, we can write:

x = undefined;

To check if a property value is undefine, we can write:

typeof obj.prop === 'undefined'

or

obj.prop === undefined

To check if a variable is undefined, we can write the following code:

typeof x === 'undefined'

A declared variable that hasn’t been assigned anything automatically has the value undefined.

If we have to check for null, then we can write:

obj.prop === null

or

x === null

for variables. We can’t use the typeof operator for checking null because the data type of null is 'object'.

14. Confusing Addition and Concatenation

In JavaScript, the + operator is used for both adding two numbers and concatenating strings together. Because JavaScript is a dynamic language, the operands are all automatically converted to the same type before the operation is applied. For example, if we have:

let x = 1 + 1;

then we get two because they’re both numbers. The + operation was used for addition like we expected. However, if we have the following expression:

let x = 1 + '1';

then we get '11' because the first operand was coerced into a string before the + operation is applied. The + operation was used for concatenation instead of addition. When we use the + operator on multiple variables, this makes knowing the type even harder. For example, if we have:

let x = 1;
let y = 2;
let z = x + y;

as we expect, we get three because x and y are both numbers. On the other hand, if we have:

let x = 1;
let y = '2';
let z = x + y;

then we get '12' because y is a string, so the + operator was used for concatenation instead. To solve this issue, we should convert all the operands to numbers first before using them with the + operator. For example, we should rewrite the above example into the following:

let x = 1;
let y = '2';
let z = Number(x) + Number(y);

The code above will get us 3 as the value of z since we converted them both to numbers with the Number factory function first. The Number function takes in any object and returns a number if it can be parsed into a number, or NaN otherwise. An alternative way to do this is to use the new Number(...).valueOf() function, as we do in the following code:

let x = 1;
let y = '2';
let z = new Number(x).valueOf() + new Number(y).valueOf();

Since new Number(...) is a constructor that creates an object type, we want to use the valueOf function to convert it back to a primitive type to make sure that what we get is a number type. A shorter way to do this is to write:

let x = 1;
let y = '2';
let z = +x + +y;

The + sign in front of a single operand will try to convert the single operand into a number or toNaN if it can’t be converted into a number. It does the same thing as the Number function. We can also convert a variable to a particular type of number. The Number object has a parseInt function to convert an object into an integer and a parseFloat function to convert an object into a floating-point number. parseInt takes the object you want to convert to a number as the first argument. It also takes a radix as an optional second argument, which is the base of the mathematical numeral systems. If the string starts with 0x, then the radix will be set to 16. If the string starts with anything else, then the radix will be set to 10.

We can use them as in the following examples:

let x = 1;
let y = '2';
let z = Number.parseInt(x) + Number.parseInt(y)

Also, we can use the parseFloat function as in the following code:

let x = 1;
let y = '2';
let z = Number.parseFloat(x) + Number.parseFloat(y)

We will get 3 in both of the examples above.

15. Breaking Return Statements Into Multiple Lines

JavaScript closes a statement at the end, so one line code is considered distinct from the other. For example, if we have:

const add = (a, b) => {
  return
  a + b;
}

we get undefined if we run console.log(add(1, 2)); since we ran the return statement, which ended the function execution, before a + b is run. Therefore, a + b will never be run in this function. To fix this, we either have to put the return statement all in one line or use parentheses to surround what we want to return. For example, we can write:

const add = (a, b) => {
  return a + b;
}

This will log 3 if we run console.log(add(1, 2)); since we are actually returning the computed result in the function. We can also write:

const add = (a, b) => {
  return (
    a + b
  );
}

This is handy for returning expressions that might be longer than one line. This will also log 3 if we run console.log(add(1, 2));. For arrow functions, we can also write:

const add = (a, b) => a + b

for single-line functions since the return statement is implicit for single-line arrow functions. We can also write:

const add = (a, b) => (a + b)

to get the same thing. This also works for single-line arrow functions.

In JavaScript, if a statement is incomplete, like the first line of:

const power = (a) => {
  const
    power = 10;
  return a ** 10;
}

inside the function then the JavaScript interpreter will run the first line together with the second line to get the full statement. So:

const
  power = 10;

is the same as:

const power = 10;

However, for complete statements like return statements, the JavaScript interpreter will treat them as separate lines. So:

return 
  a ** 10;

is not the same as:

return a ** 10;

Even though JavaScript is a friendly language, it’s still very easy to make mistakes when writing JavaScript code. It’s easy to confuse undefined and null when we aren’t familiar with JavaScript. Because of the dynamic typing nature of JavaScript, operators like the + operator that can do multiple things can easily be converted to a type we don’t expect and produce the wrong result. Also, if statements can be complete on their own, then they shouldn’t be written in their own lines if we want both lines to be together.

Yes, that is all. I hope this tutorial will surely help and you if you liked this tutorial, please consider sharing it with others. Thank you !

An Angular Roadmap — The Past, Present, and Future of Angular

An Angular Roadmap — The Past, Present, and Future of Angular

✅Interested in being an Angular developer in 2019? ... blog post it's most likely that you've written some code in javaScript in the past.

Paleolithic JavaScript — SproutCore

In the beginning, there was SproutCore. It was the first comprehensive JavaScript framework aimed at making it easy to build desktop-quality single-page web apps. It’s not that this wasn’t possible before. When Google released Gmail, it showed the world that web apps really could replace complex desktop applications. Google even open-sourced the Closure toolkit — a set of libraries and an optimizing compiler that it used to build Gmail.

The problem was that Google’s Closure tools weren’t very developer-friendly. They relied heavily on Java, which alienated web developers who were used to working with JavaScript, PHP, Ruby, and Python. Gmail was a great demonstration of what was possible, but developing similar applications still felt out of reach for many.

Some courageous developers managed to string together amazing single page apps using a combination of jQuery, duct tape, and hope. While these apps looked amazing to end-users, for the developers working on them, the apps quickly turned into hulking piles of technical debt that made the dev team dread heading to work in the morning.

As a result, a few enterprising developers began to work on frameworks that would bring Gmail-like apps within easy reach of web developers everywhere. SproutCore was the first of these frameworks to take off. It came with a complete set of widgets that made it possible to build complex web applications without even touching HTML or CSS.

This ended up being great for former desktop developers who had been dragged kicking and screaming onto the web. Several more frameworks popped up with similar goals; GWT and Cappuccino were the most prominent. These frameworks even avoided JavaScript by transpiling other languages into JS. Again, this was great for desktop developers. But it left passionate web developers out in the cold and made them feel as though their hard-won HTML, CSS, and JavaScript skills weren’t valuable.

This left an opening for a framework that truly embraced the web, instead of trying to plaster over it and pretend it was something else. A couple of early frameworks (Backbone and Knockout) appeared, and achieved a moderate amount of success. Ember also showed up around this time. It took SproutCore, stripped it down to its bones, and tried to rebuild it into something truly web-friendly. Ember wanted to be the Six Million Dollar Man of the JavaScript world: rebuilt better, stronger, and faster.

None of these frameworks rocketed to popularity. The world was waiting for something better. In 2010, that something better appeared — it was named Angular.

The Golden Age of Angular

Even before Angular version 1.0 had been released, Angular took the front-end development world by storm. Finally, we had an easy-to-use JavaScript framework that treated HTML as a first-class citizen. Developers and designers could now work together to build amazing single-page applications. This came as a relief to designers, who had been annoyed and offended because older frameworks had treated HTML and CSS as tools for barbarians, tools that no civilized developer should have to touch.

The first thing that seemed magical to developers trying Angular for the first time was two-way data binding. Prior to this, most developers had only seen this kind of data binding in desktop frameworks like WPF and Windows Forms. It was great to be able to bind forms and other UI elements to JavaScript model objects. While two-way data binding could cause performance problems when overused, teams that used it judiciously found that Angular enabled them to create complex front-end applications much more quickly than ever before.

Angular proved to be popular for more than just easy binding of data to HTML elements. Angular directives provided an easy way to create reusable HTML + CSS components. Although other JavaScript frameworks provided this before Angular, Angular was the first one that became extremely popular. Reusable components had long been in-use in server-side frameworks. ASP.NET user controls and partial templates in Rails and Django are but a few examples.

Finally, Angular made dependency injection popular in the front-end development world. Dependency injection had long been popular in enterprise applications, which is perhaps why it hadn’t caught on in the JavaScript world. Front-end developers have long been averse to what they see as needlessly complex enterprise software design patterns. This concern isn’t without merit. Have you ever, in the course of writing an application, said to yourself “What I really need here is a “SimpleBeanFactoryAwareAspectInstanceFactory?”

Dependency injection, though, has proven its worth. And Angular made dependency injection easy to use for an audience that hadn’t used it much in the past. Need an HTTP client? Or perhaps you’d like to do some animation? No problem. Angular had built-in services for those. Just ask for them, and Angular would inject them into your components. No need to instantiate anything yourself.

Or perhaps you wanted to use the browser’s window or location objects without making it impossible to unit test your components outside of a browser? Angular had you covered there too, with its built-in $window and $location services. At runtime, you’d get the browser objects you were expecting. And when running unit tests server-side in Node.js, you could pass mock services into your components to ensure they behaved as expected in various scenarios.

If all of this wasn’t enough, Angular also made it simple to register and inject your own services. For developers who were used to binding all their data to the DOM and hoping for the best, this was awesome. If you were writing a new front-end app that called for APIs that would cost your company a lot of money if overused, you’d probably prefer to be able to write tests ahead of time to ensure that your application doesn’t try to do something like calling the Twilio API 800 million times.

So you’d create a Twilio service that gets injected at runtime. At testing time, you’d create a mock service that records the cost of every API call your application is trying to make. You’d write tests that cover common usage scenarios and ensure that these scenarios don’t result in your company receiving a 7-figure bill. Overall, most developers found that Angular directives combined with dependency injection made it possible to write modular, testable front-end applications using tried-and-true software engineering techniques. Many development teams decided that this resulted in a happy state of affairs, and decided to go all-in on Angular.

The Angular Decline? The Rise of React

While things were mostly great in the world of Angular, it wasn’t all sunshine and lollipops. Developers were starting to run into severe performance problems when they tried to bind too many model objects to too many DOM elements. Some applications slowed to a crawl. Direct calls to $digest and other black-magic sorcery started becoming necessary to achieve acceptable performance. Around the same time, a new challenger appeared: React. At first, React didn’t seem to pose too large a danger to Angular. After all, these React weirdos had gone to the trouble of inventing JSX, which looked a lot like a way to mix markup into your code. Hadn’t we gone to a lot of trouble to invent templating languages for the explicit reason of avoiding mixing markup and code?

As it turned out, React’s approach was pretty popular in the front-end development community. It didn’t rocket to popularity, however. Angular was still dominant, and it looked like it would remain that way. Until that is, Angular’s popularity was given a good kick in the teeth from an unexpected source: the Angular team itself.

The Introduction of Angular 2

Angular 2 was first announced at the ng-europe conference in 2014. The Angular team’s plans came as a shock to the Angular community, to say the least. Reaction from Angular developers was swift and negative… and not without reason. Angular 2 would be getting rid of many familiar concepts from Angular 1, introducing a new, incompatible templating language (and oh, by the way) would also be programmed using an entirely new language.

AngularJS

Although both Angular 1 and Angular 2 were called ‘Angular,’ in reality, they were very different frameworks with a few things in common. To help prevent confusion, the Angular team started referring to the old version of Angular as ‘AngularJS’, and the new version as simply ‘Angular.’ This makes intuitive sense since AngularJS was written in JavaScript, and Angular was not. To keep the distinction between the frameworks clear, I’ll be referring to Angular 1 as AngularJS from this point forward.

As a result of all of this, AngularJS developers lost faith in the framework’s future. They threatened to move to a new framework on future projects, and that is precisely what many of them did. React was the biggest beneficiary of the mass exodus from AngularJS.

Although React didn’t do as much as AngularJS, in a way that was positive. If you’re using a view library that doesn’t try to include everything plus the kitchen sink, it’s a lot more difficult for the developers of that library to pull the rug out from under you in the future. In the beginning, using React was a bit of a pain compared to AngularJS. You had to cobble together a patchwork of libraries just to cover the functionality the AngularJS provided out of the box.

Many teams saw this as a good way to reduce risk, because it was unlikely that the developers of all of those libraries would decide to make backward incompatible breaking changes at the same time, which is essentially what Angular had done.

The Emergence of Vue

To compound AngularJS’ woes, another framework named Vue showed up at about the same time the drama over Angular 2 was occurring. Vue was inspired by AngularJS but aimed to simplify it and get rid of what Vue’s creator saw as unnecessary complexity (so Vue felt very familiar to existing AngularJS developers). Vue provided a safe haven for many AngularJS developers who didn’t want to move over to React.

This doesn’t mean that AngularJS developers were not waiting patiently for Angular 2 to appear. But it’s clear that there was a mass exodus from AngularJS to React and Vue due to the uncertainty caused by the plans for Angular 2.

Rising From the Ashes with Angular 2

Eventually, Angular 2 was released. As expected, it did away with many familiar concepts from AngularJS but kept a few of the best pieces like services and dependency injection. Fortunately for the sanity of developers, Angular uses plain TypeScript and not a fork as originally planned.

To make things more confusing, the Angular team maintained a fork of the new framework that used the Dart programming language instead of TypeScript. Initially, the TypeScript and Dart versions were developed in sync, generated from a single code base. Eventually, the TS and Dart versions of Angular decided to go their separate ways, and Angular Dart now exists on its own.

Even with this confusion, Angular’s popularity began to increase again after the Angular 2 release. It happened slowly. As often occurs in software development, trends shifted. Developers realized that a big, batteries-included framework might actually be useful. After all, when your application grows large enough, you end up actually needing all of those batteries.

Enterprise developers, in particular, began moving back to Angular. This makes sense. Usually, when you start an enterprise web app, you know it is going to be complex. There’s no point in starting with a tiny MVP when you know from the beginning all 87 things your application is going to be expected to do.

Where’s Angular 3?

Although Angular 2 wasn’t perfect, many developers of complex web applications began to realize that the new-and-improved Angular was a good fit for their needs. Fortunately for them, Angular had some exciting improvements in store. More importantly, the Angular team demonstrated that it could consistently publish new versions of the framework with few breaking changes between versions

In a move that seemed odd at the time, the Angular team decided to skip version 3 entirely and move to version 4. This was done for good reason: the team working on Angular’s router package had already pushed ahead and released version 3, while the remainder of Angular was still at version 2.3. They decided to keep all Angular package versions in sync moving forward, and bumping everything up to version 4 for the next release was the easiest way to achieve this.

Angular 4

Angular 4 had some significant changes, including added ahead of time compilation, which resulted in small production JavaScript bundles and shorter application load time. Support for server-side rendering was added, which was a boost for teams that wanted to render their app ahead of time to improve initial load performance. Many other improvements were added throughout the framework, but upgrading apps from Angular 2 to 4 was quick and painless in most cases.

Angular 4.3 and Angular 5

Angular 4.3 added a new HTTP client that was easier to use than the old HTTP service. In Angular 5, the old HTTP service was deprecated and would be dropped in the next release. In spite of this inconvenience, there was relatively little grumbling because the upgrade in most cases was straightforward. Angular 5 also added better internationalization support and further build optimizations.

Angular 6 and 7

Angular 6 and 7 were disappointing to some developers. There were no large changes, but there were many small quality of life improvements, especially to the Angular CLI tooling. The decreasing number of visible changes isn’t an indication that the Angular team has stopped innovating. Instead, it shows that the framework is mature, so the development team is now free to do more work behind the scenes, fixing bugs and improving performance.

The stability of the framework since the release of Angular 2 has drawn some old-school AngularJS developers back into the Angular world. It has also attracted the attention of enterprise development teams. When you’re building enterprise apps that may live for decades it’s ideal to use a framework that gets new releases on a predictable schedule but doesn’t change too rapidly. A developer who had only used Angular 2 could be up and running and contributing to an Angular 7 app within minutes.

The Future of Angular

Angular 8 and Angular Ivy

And that brings us to today. As we’ve seen, Angular has come a long way. It has gone from loved by web developers to being reviled to being admired, although it isn’t yet loved again like it was in its early days.

On the horizon, we have Angular 8. A ton of work has been done in Angular 8 to make it easy to use with the Bazel build system, which is absolutely amazing news for all 3 developers who are using it outside of Google. More excitingly, though, the Angular team is hard at work on a new rendered called Angular Ivy. It’s intended to be a drop-in replacement for the current rendered. For the most part, current apps won’t need to make any changes to use Angular Ivy.

If Angular Ivy is a drop-in replacement, why should developers care? Two important reasons: speed, and bundle size — two very important concerns. For a few years, it seemed like web developers had gone a bit crazy. Teams were shipping JavaScript bundles that were 5MB, 10MB, or even larger, and thinking that there was no problem with this. After all, the applications worked perfectly on the developers’ i7-powered MacBook Pros so they should work fine for everyone, right?

Unfortunately, in the real world, not everyone is running the latest and greatest hardware. Hundreds of millions of people access the internet solely on older Android phones with slightly more processing power than a potato, through internet connections only a little faster than dial-up. For these users, a huge JavaScript bundle takes forever to load, and even longer for their device to parse and run. Even in less extreme cases, there are countless users around the world who aren’t using the latest and greatest hardware. For them, massive JavaScript apps are usable (but painful).

Angular Ivy Expectations

The Angular Ivy renderer will help in several ways:

  1. It is being written with an eye on efficiency, so it will accomplish the same tasks while executing far fewer CPU instructions. This will improve both the battery life and the sanity of users with less-than-powerful devices.
  2. The renderer will be written in a much more modular fashion that the current renderer. This will make it much more amenable to tree-shaking and dead code elimination. As a result, your production JavaScript bundle will include only the code that is needed to run your application, instead of bundling together everything plus the kitchen sink as often happens with the current rendered.
  3. In addition to the bundle-size reduction and improved rendering speed, Angular Ivy has another few quality-of-life enhancements for Angular developers. Rebuild times are significantly faster. So if you’re running your app in development mode and waiting for your changes to appear, you’re now going to be spending a lot less time waiting.
  4. Template-type checking is improved, which means you’ll catch more errors at compile time instead of at runtime. Runtime template bugs are annoying, because they either bite you during testing, or they bite your users when they’re trying to use your app.
  5. The Angular Ivy template compiler will generate code that is human readable, which the current View Engine compiler doesn’t do. This will come in handy when trying to track down tough template bugs.

The net result: smaller apps, faster apps, happier developers, and happier users.

Angular’s Past, Present, and Future

If you’ve been using Angular from its early days all the way until now, then congratulations! While there have been plenty of rough patches, we’ve ended up with a fast, modern framework that is fun to use.

If you were an AngularJS developer but moved on to React, Vue, or something else, I encourage you to give Angular another look. It’s worth your time, even if you decide to stick with what you’re using now.

And if you’ve never used Angular at all, why not give it a shot?

We’ve just been on a whirlwind tour through Angular’s past, present, and future. Without a doubt, it has been quite a ride. Regardless of your Angular background, I hope you’ve enjoyed the tour!

30s ad

Angular 2 Firebase - Build a Web App with Typescript

Angular 2 Demystified

Master Angular 2 - The No Nonsense Course

Complete Angular 7 - Ultimate Guide - with Real World App

A Quick Guide to Angular 7 in 4 Hours