How to Validation and Controls Password in Angular

How to Validation and Controls Password in Angular

Cool Password Validation - Angular. In this post, we are going to be creating a simple signup form, with email, password and confirm password controls. We will then validate the data to ensure it fulfills our requirement before a user can submit the form.

In this post, we are going to be creating a simple signup form, with email, password and confirm password controls. We will then validate the data to ensure it fulfills our requirement before a user can submit the form. We are going to enforce the following rules for the password:

  • Must be at least 8 characters in length,
  • Must be alphanumeric, with at least one upper and one lower case character
  • And must have at least one special character.

We are going to validate each of the 5 rules individually using RegExp. And then, we will display a nice visual interface indicating to the user which rule they have not fulfilled. We will also be checking to see whether the password and the confirmation passwords are a match. Here is what the end-product will look like:

Getting Started

First, we are going to create a new project using Angular CLI.

$ ng new ng-bootstrap-password-validation-example

Then, we need to install and setup bootstrap for our angular project. First, install bootstrap using your favorite package manager:

$ npm install -s bootstrap

// or

$ yarn add bootstrap

Then, add  bootstrap SCSS Styles to the list of styles for your project inside your angular.json file. There are three of them, but only one is necessary - node_modules/bootstrap/scss/bootstrap.scss.

"styles": [
  "src/styles.css",
  "node_modules/bootstrap/scss/bootstrap-grid.scss",
  "node_modules/bootstrap/scss/bootstrap-reboot.scss",
  "node_modules/bootstrap/scss/bootstrap.scss"
],

Next, we need to add material icons to our angular project. Open your index.html (Located inside the src directory at the root of your angular workspace) and add the following link:

<link
  href="https://fonts.googleapis.com/icon?family=Material+Icons"
  rel="stylesheet"
/>

This is the easiest way to add material icons to your project. If you are interested in learning of the other ways of adding material icons to any project, you can visit the official guide here. And finally, we need to vertically re-align material icons, so they appear more centralized. We will use the following CSS code:

.material-icons {
   display: inline-flex;
   vertical-align: middle;
}

Add the above code in your app styles, by default its styles.css located under the src directory at the root of your angular workspace. The last thing we need to do is import the modules we need in our app module. In this project, we are going to require ReactiveFormsModule only, since we are building a reactive form. So, let’s add that module to our imports in the app module (By default app.module.ts).

@NgModule({
  ...
  imports: [
   BrowserModule,
   ReactiveFormsModule
  ],
  ...
})
export class AppModule {}
Building a Custom Validator

Angular provides built in validators, but in our case they won’t help us achieve what we want. We need a custom validator that uses regex to check whether a password contains a special character or a number and report the error to back to us.

NB: If you want to learn more about creating a custom validator, please visit the following link where I cover it in greater details.

So, our custom validator will accept the validation RegExp expression, and validation error object to return if it encounters an error. For instance, if we want to check whether it has a number, we will pass the following:

patternValidator(/\d/, { hasNumber: true }),

Where /\d/ is the RegExp expression for checking if it has a number and { hasNumber: true } is our error to return. The error part of our parameters will allow us to use the control.hasError("hasNumber") within our template, when checking whether to show the error. Now, let’s build our validator. First, we are going to create a CustomValidators class, where we can place multiple custom validator methods for our project.

$ ng g class custom-validators

Then, we are going to add a static pattern validator (patternValidator) method.

NB: Follow the comments to see what individual lines of code do.

static patternValidator(regex: RegExp, error: ValidationErrors): ValidatorFn {
  return (control: AbstractControl): { [key: string]: any } => {
    if (!control.value) {
      // if control is empty return no error
      return null;
    }

    // test the value of the control against the regexp supplied
    const valid = regex.test(control.value);

    // if true, return no error (no error), else return error passed in the second parameter
    return valid ? null : error;
  };
}

And we also need a second CustomValidator to check whether our password and confirm password are a match. Add a second customValidator, called passwordMatchValidator and add the following code.

NB: You can follow the comments on the code for what individual lines do.

static passwordMatchValidator(control: AbstractControl) {
  const password: string = control.get('password').value; // get password from our password form control
  const confirmPassword: string = control.get('confirmPassword').value; // get password from our confirmPassword form control
  // compare is the password math
  if (password !== confirmPassword) {
    // if they don't match, set an error in our confirmPassword form control
    control.get('confirmPassword').setErrors({ NoPassswordMatch: true });
  }
}
Building Our Signup Form

For this project, we are going to use the default component – AppComponent.

Component Class

First, we need to inject the FormBuilder in to our component.

constructor(private fb: FormBuilder) {}

Next, we need to add a new property in our component class – frmSignup, that will hold information about our form:

public frmSignup: FormGroup;

Next, we need to create a method for adding form controls to the frmSignup property we just created above: We call the method createSignupForm, and it will return a FormGroup class.

createSignupForm(): FormGroup {}

Then inside the createSignupForm() method above, we need to add the controls for our form and the validation rules required.

NB: Follow the comments below for more information about individual lines of code.

createSignupForm(): FormGroup {
  return this.fb.group(
    {
      // email is required and must be a valid email email
      email: [null, Validators.compose([
         Validators.email,
         Validators.required])
      ],
      password: [ null, Validators.compose([
         // 1\. Password Field is Required
         Validators.required,
         // 2\. check whether the entered password has a number
         CustomValidators.patternValidator(/\d/, { hasNumber: true }),
         // 3\. check whether the entered password has upper case letter
         CustomValidators.patternValidator(/[A-Z]/, { hasCapitalCase: true }),
         // 4\. check whether the entered password has a lower-case letter
         CustomValidators.patternValidator(/[a-z]/, { hasSmallCase: true }),
         // 5\. check whether the entered password has a special character
         CustomValidators.patternValidator(/[ [[email protected]#$%^&*()_+-=[]{};':"|,.<>/?]/](<mailto:[email protected]#$%^&*()_+-=[]{};':"|,.<>/?]/>), { hasSpecialCharacters: true }),
         // 6\. Has a minimum length of 8 characters
         Validators.minLength(8)])
      ],
      confirmPassword: [null, Validators.compose([Validators.required])]
   },
   {
      // check whether our password and confirm password match
      validator: CustomValidators.passwordMatchValidator
   });
}

Are you still with me? Now let’s move to our component template.

Component Template:

We will create a normal reactive form as you normally would:

<form [formGroup]="frmSignup" (submit)="submit()"></form>

Then, we add our individual form controls:

<input id="email" formControlName="email" type="email" class="form-control" />

Then, we need to show validation errors to users. For instance, to check whether the email provided is valid, we use the following expression:

frmSignup.controls['email'].hasError('email')

And the same for a required form field:

frmSignup.controls['email'].hasError('required')

And to display a message, we can simply use *ngIf:

<label
  class="text-danger"
  *ngIf="frmSignup.controls['email'].hasError('email')"
>
  Enter a valid email address!
</label>

NB: This will display the Enter a valid Email Address! error only when there is such an error. You can do the same for required, and custom validation errors.

The same goes for confirming whether confirm password and password are a match:

<label
  class="text-danger"
  *ngIf="frmSignup.controls['confirmPassword'].hasError('NoPassswordMatch')"
>
  Password do not match
</label>

We can also add a red border around our form field to give the error some prominence. We are going to add is-invalid bootstrap class using ngClass, adding the class when the form control has an error and remove it when there is no error:

[ngClass]="frmSignup.controls['email'].invalid ? 'is-invalid' : ''"

So, now our form field looks like this:

<input
  id="email"
  formControlName="email"
  type="email"
  class="form-control"
  [ngClass]="frmSignup.controls['email'].invalid ? 'is-invalid' : ''"
/>

Password Rules Validation

There is not much difference from the above code when it customs to custom validation rules.

<label
  class="text-danger"
  *ngIf="frmSignup.controls['password'].hasError('hasNumber')"
>
  Must have at least 1 number!
</label>

But, since we don’t want to just hide and show the errors as with other form fields, we want to show text with green font for rules the password has fulfilled, and a red font color for rules not fulfilled. So, instead of *ngIf, we will use a ngClass, to switch between bootstrap classes  text-success and text-danger.

[ngClass]="frmSignup.controls['password'].hasError('required') ||
frmSignup.controls['password'].hasError('hasNumber')  ? 'text-danger' :
'text-success'"

We also need to do the same for icons, showing a check icon when a rule has been fulfilled and cancel icon otherwise.

<i class="material-icons">
  {{ frmSignup.controls['password'].hasError('required') ||
  frmSignup.controls['password'].hasError('hasNumber') ? 'cancel' :
  'check_circle' }}
</i>

So, our complete code will look like this for checking whether password has a number:

<label
  class="col"
  [ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasNumber')  ? 'text-danger' :'text-success'"
>
  <i class="material-icons">
    {{
    frmSignup.controls['password'].hasError('frmSignup.controls['password'].hasError('hasNumber')
    ? 'cancel' : 'check_circle' }}
  </i>
  Must contain atleast 1 number!
</label>

And the same goes for our other password rules:

<label
  [ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('minlength')  ? 'text-danger' : 'text-success'"
>
  <i class="material-icons">
    {{ frmSignup.controls['password'].hasError('required') ||
    frmSignup.controls['password'].hasError('minlength') ? 'cancel' :
    'check_circle' }}
  </i>
  Must be at least 8 characters!
</label>

<label
  class="col"
  [ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasNumber')  ? 'text-danger' : 'text-success'"
>
  <i class="material-icons">
    {{ frmSignup.controls['password'].hasError('required') ||
    frmSignup.controls['password'].hasError('hasNumber') ? 'cancel' :
    'check_circle' }}
  </i>
  Must contain at least 1 number!
</label>

<label
  class="col"
  [ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasCapitalCase')  ? 'text-danger' : 'text-success'"
>
  <i class="material-icons">
    {{ frmSignup.controls['password'].hasError('required') ||
    frmSignup.controls['password'].hasError('hasCapitalCase') ? 'cancel' :
    'check_circle' }}
  </i>
  Must contain at least 1 in Capital Case!
</label>

<label
  class="col"
  [ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasSmallCase')  ? 'text-danger' : 'text-success'"
>
  <i class="material-icons">
    {{ frmSignup.controls['password'].hasError('required') ||
    frmSignup.controls['password'].hasError('hasSmallCase') ? 'cancel' :
    'check_circle' }}
  </i>
  Must contain at least 1 Letter in Small Case!
</label>

<label
  class="col"
  [ngClass]="frmSignup.controls['password'].hasError('required') || frmSignup.controls['password'].hasError('hasSpecialCharacters') ? 'text-danger' : 'text-success'"
>
  <i class="material-icons">
    {{ frmSignup.controls['password'].hasError('required') ||
    frmSignup.controls['password'].hasError('hasSpecialCharacters') ? 'cancel' :
    'check_circle' }}
  </i>
  Must contain at least 1 Special Character!
</label>
Demo and Source Code

You can find a demo to play with here and the complete source code for the above project here.

Thank you ! Happy coding !

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript. This tutorial gets you off the ground with Angular. We are going to use the official CLI (command line) tool to generate boilerplate code.

Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript. This tutorial gets you off the ground with Angular. We are going to use the official CLI (command line) tool to generate boilerplate code.

1. Prerequisites

This tutorial is targeted to people familiar with JavaScript and HTML/CSS. You also will need:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

You can verify by typing:

node --version
# v10.8.0
npm --version
# 6.2.0

If you get the versions Node 4.x.x and NPM 3.x.x. or higher you are all set. If not you have to get the latest versions.

Let’s move on to Angular. We are going to create a Todo app.

2. Understanding ng new

Angular CLI is the best way to get us started. We can download the tool and create a new project by running:

# install angular-cli globally
npm install -g @angular/[email protected]
# npm install -g @angular/cli # get latest

# Check angular CLI is installed
ng --version
# Angular CLI: 6.1.2

If the versions don’t match then you can remove previously installed angular CLI with the following commands:

npm uninstall -g @angular/cli
yarn global remove @angular/cli

Once you have the right version, do:

# create a new project
ng new Todos --style=scss

Note The last command takes some minutes. Leave it running and continue reading this tutorial.

The command ng new will do a bunch of things for us:

  1. Initialize a git repository
  2. Creates an package.json files with all the Angular dependencies.
  3. Setup TypeScript, Webpack, Tests (Jasmine, Protractor, Karma). Don’t worry if you don’t know what they are. We are going to cover them later.
  4. It creates the src folder with the bootstrapping code to load our app into the browser
  5. Finally, it does an npm install to get all the packages into node_modules.

Let’s run the app!

# builds the app and run it on port 9000
ng serve ---port 9000

Open your browser on http://localhost:9000/, and you should see “Loading…” and then it should switch to “Welcome to app!”. Awesome!

Now let’s dive into the src folder and get familiarized with the structure.

2.1 package.json

Open the package.json file and take a look at the dependencies. We have all the angular dependencies with the prefix @angular/.... Other dependencies are needed for Angular to run, such as RxJS, Zone.js, and some others. We are going to cover them in other posts.

2.2 src/index.html

We are building an SPA (single page application), so everything is going to be loaded into the index.html. Let’s take a look in the src/index.html. It’s pretty standard HTML5 code, except for two elements that are specific for our app:

  1. Initialize a git repository
  2. Creates an package.json files with all the Angular dependencies.
  3. Setup TypeScript, Webpack, Tests (Jasmine, Protractor, Karma). Don’t worry if you don’t know what they are. We are going to cover them later.
  4. It creates the src folder with the bootstrapping code to load our app into the browser
  5. Finally, it does an npm install to get all the packages into node_modules.

base href is needed for Angular routing to work correctly. We are going to cover Routing later.

<app-root> this is not a standard HTML tag. Our Angular App defines it. It’s an Angular component. More on this later.

2.3 src/main.ts

main.ts is where our application starts bootstrapping (loading). Angular can be used not just in browsers, but also on other platforms such as mobile apps or even desktop apps. So, when we start our application, we have to specify what platform we want to target. That’s why we import: platform-browser-dynamic. Notice that we are also importing the AppModule from ./app.

The most important line is:

platformBrowserDynamic().bootstrapModule(AppModule);

We are loading our AppModule into the browser platform. Now, let’s take a look at the ./app/app.module.tsdirectory.

2.4 App directory

The app directory contains the components used to mount the rest of the application. In there the <app-root> that we so in the index.html is defined. Let’s start with app.module

app.module.ts

We are going to be using this file often. The most important part is the metadata inside the @NgModule. There we have declarationsimportsproviders and bootstrap.

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

app.component.ts

AppComponent looks a little similar to the app module, but instead of @NgModule we have @Component. Again, the most important part is the value of the attributes (metadata). We have selectortemplateUrl and styleUrls:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

Inside the AppComponent class you can define variables (e.g. title) that are used in the templates (e.g. Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript).

Let’s change the title from Welcome to Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript!to Angular Tutorial: Create a CRUD App with Angular CLI and TypeScript. Also, remove everything else.
Test your changes running:

ng serve ---port 9000

You should see the new message.

[changes diff]

3. Creating a new Component with Angular CLI

Let’s create a new component to display the tasks. We can quickly create by typing:

ng generate component todo

This command will create a new folder with four files:

create src/app/todo/todo.component.css
create src/app/todo/todo.component.html
create src/app/todo/todo.component.spec.ts
create src/app/todo/todo.component.ts

And it will add the new Todo component to the AppModule:

UPDATE src/app/app.module.ts

Go ahead and inspect each one. It will look similar to the app components. Let ‘s add our new component to the App component.

[changes diff]

Go to src/app/app.component.html, and replace everything with:

src/app/app.component.html

<app-todo></app-todo>

If you have ng serve running, it should automatically update and show todo works!

[changes diff]

4. Todo Template

“todo works!” is not useful. Let’s change that by adding some HTML code to represent our todo tasks. Go to the src/app/todo/todo.component.html file and copy-paste this HTML code:

<section class="todoapp">

  <header class="header">
    <h1>Todo</h1>
    <input class="new-todo" placeholder="What needs to be done?" autofocus>
  </header>

  <!-- This section should be hidden by default and shown when there are todos -->
  <section class="main">

    <ul class="todo-list">
      <!-- These are here just to show the structure of the list items -->
      <!-- List items should get the class `editing` when editing and `completed` when marked as completed -->
      <li class="completed">
        <div class="view">
          <input class="toggle" type="checkbox" checked>
          <label>Install angular-cli</label>
          <button class="destroy"></button>
        </div>
        <input class="edit" value="Create a TodoMVC template">
      </li>
      <li>
        <div class="view">
          <input class="toggle" type="checkbox">
          <label>Understand Angular2 apps</label>
          <button class="destroy"></button>
        </div>
        <input class="edit" value="Rule the web">
      </li>
    </ul>
  </section>

  <!-- This footer should hidden by default and shown when there are todos -->
  <footer class="footer">
    <!-- This should be `0 items left` by default -->
    <span class="todo-count"><strong>0</strong> item left</span>
    <!-- Remove this if you don't implement routing -->
    <ul class="filters">
      <li>
        <a class="selected" href="#/">All</a>
      </li>
      <li>
        <a href="#/active">Active</a>
      </li>
      <li>
        <a href="#/completed">Completed</a>
      </li>
    </ul>
    <!-- Hidden if no completed items are left ↓ -->
    <button class="clear-completed">Clear completed</button>
  </footer>
</section>

The above HTML code has the general structure about how we want to represent our tasks. Right now it has hard-coded todo’s. We are going to slowly turn it into a dynamic app using Angular data bindings.

[changes diff]

Next, let’s add some styling!

5. Styling the todo app

We are going to use a community maintained CSS for Todo apps. We can go ahead and download the CSS:

npm install --save todomvc-app-css

This will install a CSS file that we can use to style our Todo app and make it look nice. In the next section, we are going to explain how to use it with the angular-cli.json.

6. Adding global styles to angular.json

angular.json is a special file that tells the Angular CLI how to build your application. You can define how to name your root folder, tests and much more. What we care right now, is telling the angular CLI to use our new CSS file from the node modules. You can do it by adding the following line into the styles array:

"architect": {
  "build": {
    "options": {
      "styles": [
        "src/styles.scss",
        "node_modules/todomvc-app-css/index.css"
      ],
      "scripts": []

If you stop and start ng serve, then you will notice the changes.

We have the skeleton so far. Now we are going to make it dynamic and allow users to add/remove/update/sort tasks. We are going to do two versions one serverless and another one using a Node.js/Express server. We are going to be using promises all the time, so when we use a real API, the service is the only one that has to change.

[changes diff]

7. Todo Service

Let’s first start by creating a service that contains an initial list of tasks that we want to manage. We are going to use a service to manipulate the data. Let’s create the service with the CLI by typing:

ng g service todo/todo

This will create two files:

create src/app/todo/todo.service.spec.ts
create src/app/todo/todo.service.ts

[changes diff]

8. CRUD Functionality

For enabling the create-read-update-delete functionality, we are going to be modifying three files:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

Let’s get started!

8.1 READ: Get all tasks

Let’s modify the todo.service to be able to get tasks:

import { Injectable } from '@angular/core';

const TODOS = [
  { title: 'Install Angular CLI', isDone: true },
  { title: 'Style app', isDone: true },
  { title: 'Finish service functionality', isDone: false },
  { title: 'Setup API', isDone: false },
];

@Injectable({
  providedIn: 'root'
})
export class TodoService {

  constructor() { }

  get() {
    return new Promise(resolve => resolve(TODOS));
  }
}

Now we need to change our todo component to use the service that we created.

import { Component, OnInit } from '@angular/core';
import { TodoService } from './todo.service';

@Component({
  selector: 'app-todo',
  templateUrl: './todo.component.html',
  styleUrls: ['./todo.component.scss'],
  providers: [TodoService]
})
export class TodoComponent implements OnInit {
  private todos;
  private activeTasks;

  constructor(private todoService: TodoService) { }

  getTodos(){
    return this.todoService.get().then(todos => {
      this.todos = todos;
      this.activeTasks = this.todos.filter(todo => todo.isDone).length;
    });
  }

  ngOnInit() {
    this.getTodos();
  }
}

The first change is importing our TodoService and adding it to the providers. Then we use the constructor of the component to load the TodoService. While we inject the service, we can hold a private instance of it in the variable todoService. Finally, we use it in the getTodos method. This will make a variable todos available in the template where we can render the tasks.

Let’s change the template so we can render the data from the service. Go to the todo.component.html and change what is inside the <ul class="todo-list"> ... </ul> for this one:

<ul class="todo-list">
  <li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone}" >
    <div class="view">
      <input class="toggle" type="checkbox" [checked]="todo.isDone">
      <label>{{todo.title}}</label>
      <button class="destroy"></button>
    </div>
    <input class="edit" value="{{todo.title}}">
  </li>
</ul>

Also change the 32 in the template from:

<span class="todo-count"><strong>0</strong> item left</span>

replace it with:

<span class="todo-count"><strong>{{activeTasks}}</strong> item left</span>

When your browser updates you should have something like this:

Now, let’s go over what we just did. We can see that we added new data-binding into the template:

  • Node.js up and running.
  • NPM (Node package manager) or Yarn installed.

[changes diff]

8.2 CREATE: using the input form

Let’s start with the template this time. We have an input element for creating new tasks. Let’s listen to changes in the input form and when we click enter it creates the TODO.

<input class="new-todo"
       placeholder="What needs to be done?"
       [(ngModel)]="newTodo"
       (keyup.enter)="addTodo()"
       autofocus>

Notice that we are using a new variable called newTodo and method called addTodo(). Let’s go to the controller and give it some functionality:

private newTodo;

addTodo(){
  this.todoService.add({ title: this.newTodo, isDone: false }).then(() => {
    return this.getTodos();
  }).then(() => {
    this.newTodo = ''; // clear input form value
  });
}

First, we created a private variable that we are going to use to get values from the input form. Then we created a new todo using the todo service method add. It doesn’t exist yet, so we are going to create it next:

add(data) {
  return new Promise(resolve => {
    TODOS.push(data);
    resolve(data);
  });
}

The above code adds the new element into the todos array and resolves the promise. That’s all. Go ahead a test it out creating a new todo element.

You might get an error saying:

Can't bind to 'ngModel' since it isn't a known property of 'input'

To use the two-way data binding you need to import FormsModule in the app.module.ts. So let’s do that.

import { FormsModule } from '@angular/forms';

// ...

@NgModule({
  imports: [
    // ...
    FormsModule
  ],
  // ...
})

Now it should add new tasks to the list!

[changes diff]

8.3 UPDATE: on double click

Let’s add an event listener to double-click on each todo. That way, we can change the content. Editing is tricky since we need to display an input form. Then when the user clicks enter it should update the value. Finally, it should hide the input and show the label with the updated value. Let’s do that by keeping a temp variable called editing which could be true or false.

<li *ngFor="let todo of todos" [ngClass]="{completed: todo.isDone, editing: todo.editing}" >
  <div class="view">
    <input class="toggle" type="checkbox" [checked]="todo.isDone">
    <label (dblclick)="todo.editing = true">{{todo.title}}</label>
    <button class="destroy"></button>
  </div>
  <input class="edit"
         #updatedTodo
         [value]="todo.title"
         (blur)="updateTodo(todo, updatedTodo.value)"
         (keyup.escape)="todo.editing = false"
         (keyup.enter)="updateTodo(todo, updatedTodo.value)">
</li>

Notice that we are adding a local variable in the template #updateTodo. Then we use it to get the value like updateTodo.value and pass it to a function. We want to update the variables on blur (when you click somewhere else) or on enter. Let’s add the function that updates the value in the component.

Also, notice that we have a new CSS class applied to the element called editing. This is going to take care through CSS to hide and show the input element when needed.

updateTodo(todo, newValue) {
  todo.title = newValue;
  return this.todoService.put(todo).then(() => {
    todo.editing = false;
    return this.getTodos();
  });
}

We update the new todo’s title, and after the service has processed the update, we set editing to false. Finally, we reload all the tasks again. Let’s add the put action on the service.

put(changed) {
  return new Promise(resolve => {
    const index = TODOS.findIndex(todo => todo === changed);
    TODOS[index].title = changed.title;
    resolve(changed);
  });
}

Now, we can edit tasks! Yay!

[changes diff]

8.4 DELETE: clicking X

This is like the other actions. We add an event listenter on the destroy button:

<button class="destroy" (click)="destroyTodo(todo)"></button>

Then we add the function to the component:

destroyTodo(todo) {
  this.todoService.delete(todo).then(() => {
    return this.getTodos();
  });
}

and finally, we add the method to the service:

delete(selected) {
  return new Promise(resolve => {
    const index = TODOS.findIndex(todo => todo === selected);
    TODOS.splice(index, 1);
    resolve(true);
  });
}

Now test it out in the browser!

[changes diff]

9. Routing and Navigation

It’s time to activate the routing. When we click on the active button, we want to show only the ones that are active. Similarly, we want to filter by completed. Additionally, we want to the filters to change the route /active or /completed URLs.

In AppModule, we need to add the router library and define the routes as follows:

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { HttpModule } from '@angular/http';
import { Routes, RouterModule } from '@angular/router';

import { AppComponent } from './app.component';
import { TodoComponent } from './todo/todo.component';

const routes: Routes = [
  { path: ':status', component: TodoComponent },
  { path: '**', redirectTo: '/all' }
];

@NgModule({
  declarations: [
    AppComponent,
    TodoComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule,
    RouterModule.forRoot(routes)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

First, we import the routing library. Then we define the routes that we need. We could have said path: 'active', component: TodoComponent and then repeat the same for completed. But instead, we define a parameter called :status that could take any value (allcompletedactive). Any other value path we are going to redirect it to /all. That’s what the ** means.

Finally, we add it to the imports. So the app module uses it. Since the AppComponent is using routes, now we need to define the <router-outlet>. That’s the place where the routes are going to render the component based on the path (in our case TodoComponent).

Let’s go to app/app.component.html and replace <app-todo></app-todo> for <router-outlet></router-outlet>:

<router-outlet></router-outlet>

Test the app in the browser and verify that now the URL is by default [http://localhost:9000/all](http://localhost:9000/all "http://localhost:9000/all").

[changes diff]

9.1 Using routerLink and ActivatedRoute

routerLink is the replacement of href for our dynamic routes. We have set it up to be /all/complete and /active. Notice that the expression is an array. You can pass each part of the URL as an element of the collection.

<ul class="filters">
  <li>
    <a [routerLink]="['/all']" [class.selected]="path === 'all'">All</a>
  </li>
  <li>
    <a [routerLink]="['/active']" [class.selected]="path === 'active'">Active</a>
  </li>
  <li>
    <a [routerLink]="['/completed']" [class.selected]="path === 'completed'">Completed</a>
  </li>
</ul>

What we are doing is applying the selected class if the path matches the button. Yet, we haven’t populate the the path variable yet. So let’s do that:

import { Component, OnInit } from '@angular/core';
import { ActivatedRoute } from '@angular/router';

import { TodoService } from './todo.service';

@Component({
  selector: 'app-todo',
  templateUrl: './todo.component.html',
  styleUrls: ['./todo.component.scss'],
  providers: [TodoService]
})
export class TodoComponent implements OnInit {
  private todos;
  private activeTasks;
  private newTodo;
  private path;

  constructor(private todoService: TodoService, private route: ActivatedRoute) { }

  ngOnInit() {
    this.route.params.subscribe(params => {
      this.path = params['status'];
      this.getTodos();
    });
  }

  /* ... */
}

We added ActivatedRoute as a dependency and in the constructor. ActivatedRoute gives us access to the all the route params such as path. Notice that we are using it in the NgOnInit and set the path accordantly.

Go to the browser and check out that the URL matches the active button. But, it doesn’t filter anything yet. Let’s fix that.

[changes diff]

9.2 Filtering data based on the route

To filter todos by active and completed, we need to pass a parameter to the todoService.get.

ngOnInit() {
  this.route.params.subscribe(params => {
    this.path = params['status'];
    this.getTodos(this.path);
  });
}

getTodos(query = ''){
  return this.todoService.get(query).then(todos => {
    this.todos = todos;
    this.activeTasks = this.todos.filter(todo => todo.isDone).length;
  });
}

We added a new parameter query, which takes the path (active, completed or all). Then, we pass that parameter to the service. Let’s handle that in the service:

get(query = '') {
  return new Promise(resolve => {
    let data;

    if (query === 'completed' || query === 'active'){
      const isCompleted = query === 'completed';
      data = TODOS.filter(todo => todo.isDone === isCompleted);
    } else {
      data = TODOS;
    }

    resolve(data);
  });
}

So we added a filter by isDone when we pass either completed or active. If the query is anything else, we return all the todos tasks. That’s pretty much it, test it out!

[changes diff]

10. Clearing out completed tasks

One last UI functionality, clearing out completed tasks button. Let’s first add the click event on the template:

<button class="clear-completed" (click)="clearCompleted()">Clear completed</button>

We referenced a new function clearCompleted that we haven’t create yet. Let’s create it in the TodoComponent:

clearCompleted() {
  this.todoService.deleteCompleted().then(() => {
    return this.getTodos();
  });
}

In the same way we have to create deleteCompleted in the service:

deleteCompleted() {
  return new Promise(resolve => {
    todos = todos.filter(todo => !todo.isDone);
    resolve(todos);
  });
}

We use the filter to get the active tasks and replace the todos array with it.

That’s it we have completed all the functionality.

[changes diff]

11. Deploying the app

You can generate all your assets for production running this command:

ng build --prod

It will minify and concatenate the assets for serving the app faster.

If you want to deploy to a Github page you can do the following:

ng build --prod --output-path docs --base-href "/angular-todo-app/"

Replace /angular-todo-app/ with the name of your project name. Finally, go to settings and set up serving Github pages using the /docs folder:

12. Troubleshooting

If when you compile for production you get an error like:

The variable used in the template needs to be declared as "public". Template is treated as a separate Typescript class.

ERROR in src/app/todo/todo.component.html(7,8): : Property 'newTodo' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(19,11): : Property 'todos' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(38,38): : Property 'activeTasks' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(41,36): : Property 'path' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(44,39): : Property 'path' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(47,42): : Property 'path' is private and only accessible within class 'TodoComponent'.
src/app/todo/todo.component.html(7,8): : Property 'newTodo' is private and only accessible within class 'TodoComponent'.

Then you need to change private to public like this. This is because the Template in Angular is treated like a separate class.

That’s all folks!

==================================

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

Learn More

☞ Angular 8 (formerly Angular 2) - The Complete Guide

☞ Learn and Understand AngularJS

☞ The Complete Angular Course: Beginner to Advanced

☞ Angular Crash Course for Busy Developers

☞ Angular Essentials (Angular 2+ with TypeScript)

☞ Angular (Full App) with Angular Material, Angularfire & NgRx

☞ Angular & NodeJS - The MEAN Stack Guide

TypeScript, Angular, Firebase & Angular Material Master class Tutorial

As the course progresses, you'll get familiar with: TypeScript, Angular Application Architecture, and Angular CLI. Angular Modules and Angular Components. Angular's Component LifeCycle Hooks....

As the course progresses, you'll get familiar with: TypeScript, Angular Application Architecture, and Angular CLI. Angular Modules and Angular Components. Angular's Component LifeCycle Hooks....

Learn More

Angular 7 (formerly Angular 2) - The Complete Guide

Learn and Understand AngularJS

Angular Crash Course for Busy Developers

The Complete Angular Course: Beginner to Advanced

Angular (Angular 2+) & NodeJS - The MEAN Stack Guide

Become a JavaScript developer - Learn (React, Node,Angular)

Angular (Full App) with Angular Material, Angularfire & NgRx

Thanks for reading :heart: If you liked this post, share it with all of your programming buddies! Follow me on Facebook | Twitter

How to Add Authentication to Your Angular Application

How to Add Authentication to Your Angular Application

In this article, we discuss how to implement authentication, you’ll use Okta for authentication and access control. Be sure to take advantage of Okta’s Angular-specific libraries that make the entire process easier.

A Single-Page Application (SPA) is a web app or website that interacts with a user by rewriting current pages instead of loading entirely new pages from the server. While building a SPA, the developer, sooner or later, needs to think about authentication and authorization. Authentication usually requires a login page that can verify that a user is who he or she claims to be. After the user logs in, authorization works to grant or restrict specific resources.

Today, I’ll walk you through the steps to build an Angular application with authentication. You’ll use Okta for authentication and access control. Be sure to take advantage of Okta’s Angular-specific libraries that make the entire process easier.

Build an Angular SPA With Login

In this tutorial, I will focus purely on client-side security. I will not delve into the topic of server-side authentication or authorization. The application you will be implementing is a simple serverless online calculator. Access to the calculator will be restricted to users who have logged in. Naturally, real-life applications will communicate with the server and authenticate themselves with the server to gain access to restricted resources.

I will assume that you have installed Node on your system and that you are somewhat familiar with the node packet manager npm. The tutorial will use Angular 7. To install the Angular command line tool, open a terminal and enter the command:

npm install -g @angular/[email protected]

This will install the global ng command. On some systems, Node installs global commands in a directory that is not writable by regular users. In this case, you have to run the command above using sudo. Next, create a new Angular application. Navigate to a directory of your choice, and issue the following command.

ng new AngularCalculator

This starts a wizard that will walk you through creating a new application. The wizard will prompt you with two questions. When asked whether to add Angular routing in your application, answer yes. Next, you are given the choice of CSS technology. Choose plain CSS here, since the application you will be developing requires a small amount of styling. The next step is to install some libraries that will make it easier to create a pleasant, responsive design. Change into the AngularCalculator directory that you just created and run the command:

ng add @angular/material

This will prompt you with a few options. In the first question, you can choose the color theme. (I chose Deep Purple/Amber.) For the next two questions, answer yes to both using gesture recognition and browser animations. On top of Material Design, I will be using Flex Layout components. Run the command:

npm install @angular/[email protected]

Next, add some general styling to the application. Open src/style.css and replace the contents with the following.

body {
  margin: 0;
  font-family: sans-serif;
}

h1, h2 {
  text-align: center;
}

The next step is to make the Material Design components available inside the Angular application. Replace the contents of the file src/app/app.module.ts with the following code.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule } from '@angular/common/http';

import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { FlexLayoutModule } from "@angular/flex-layout";

import { MatToolbarModule,
         MatMenuModule,
         MatIconModule,
         MatCardModule,
         MatButtonModule,
         MatTableModule,
         MatDividerModule } from '@angular/material';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent,
  ],
  imports: [
    BrowserModule,
    HttpClientModule,
    BrowserAnimationsModule,
    FlexLayoutModule,
    MatToolbarModule,
    MatMenuModule,
    MatIconModule,
    MatCardModule,
    MatButtonModule,
    MatTableModule,
    MatDividerModule,
    AppRoutingModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

The file src/app/app.component.html contains the template for the main application component. This component acts as a container for the application and all its components. I like to create a basic toolbar layout in this component. Open the file and replace the content with the following.

<mat-toolbar color="primary" class="expanded-toolbar">
  <span>
    <button mat-button routerLink="/">{{title}}</button>
    <button mat-button routerLink="/"><mat-icon>home</mat-icon></button>
  </span>
  <button mat-button routerLink="/calculator"><mat-icon>dialpad</mat-icon></button>
</mat-toolbar>
<router-outlet></router-outlet>

To add some styling, open src/app/app.component.css and add the following lines.

.expanded-toolbar {
  justify-content: space-between;
  align-items: center;
}

The <router-outlet> tag in the HTML template acts as a placeholder for the components that are managed by the router. Create these components next. The application will be made up of two views. The home view shows a simple splash screen containing information about the application. The calculator component contains the actual calculator. To create the components for these views, open the terminal again in the application’s main directory and run the ‘ng generate’ command for each.

ng generate component home
ng generate component calculator

This will create two directories under src/app, one for each component. You will only add two simple headers to the splash screen. Open src/app/home/home.component.html and replace the content with the following.

<h1>Angular Calculator</h1>
<h2>A simple calculator with login</h2>

The calculator component contains the main meat of the application. Start by creating the layout template for the calculator’s buttons and display in src/app/calculator/calculator.component.html.

<h1 class="h1">Calculator</h1>
<div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="center" class="products">
  <mat-card class="mat-elevation-z1 calculator">
    <p class="display">{{display}}</p>
    <div>
      <button mat-raised-button color="warn" (click)="acPressed()">AC</button>
      <button mat-raised-button color="warn" (click)="cePressed()">CE</button>
      <button mat-raised-button color="primary" (click)="percentPressed()">%</button>
      <button mat-raised-button color="primary" (click)="operatorPressed('/')">÷</button>
    </div>
    <div>
      <button mat-raised-button (click)="numberPressed('7')">7</button>
      <button mat-raised-button (click)="numberPressed('8')">8</button>
      <button mat-raised-button (click)="numberPressed('9')">9</button>
      <button mat-raised-button color="primary" (click)="operatorPressed('*')">x</button>
    </div>
    <div>
      <button mat-raised-button (click)="numberPressed('4')">4</button>
      <button mat-raised-button (click)="numberPressed('5')">5</button>
      <button mat-raised-button (click)="numberPressed('6')">6</button>
      <button mat-raised-button color="primary" (click)="operatorPressed('-')">-</button>
    </div>
    <div>
      <button mat-raised-button (click)="numberPressed('1')">1</button>
      <button mat-raised-button (click)="numberPressed('2')">2</button>
      <button mat-raised-button (click)="numberPressed('3')">3</button>
      <button mat-raised-button color="primary" class="tall" (click)="operatorPressed('+')">+</button>
    </div>
    <div>
      <button mat-raised-button (click)="numberPressed('0')">0</button>
      <button mat-raised-button (click)="numberPressed('.')">.</button>
      <button mat-raised-button color="primary" (click)="equalPressed()">=</button>
    </div>
  </mat-card>
</div>

You will notice the (click) property on the buttons. This property allows you to specify the member functions of the component’s class that will be called when the button is clicked. Before you get around to implementing that class, add a little bit of styling in src/app/calculator/calculator.component.css.

.display {
  background-color: #f8f8f8;
  line-height: 24px;
  padding: 5px 8px;
}

.calculator button {
  margin: 5px;
  width: 64px;
}

.calculator button.tall {
  float: right;
  height: 82px;
}

The component’s class lives in the file src/app/calculator/calculator.component.ts. Open this file and replace its contents with the following code.

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-calculator',
  templateUrl: './calculator.component.html',
  styleUrls: ['./calculator.component.css']
})
export class CalculatorComponent implements OnInit {
  private stack: (number|string)[];
  display: string;

  constructor() { }

  ngOnInit() {
    this.display = '0';
    this.stack = ['='];
  }

  numberPressed(val: string) {
    if (typeof this.stack[this.stack.length - 1] !== 'number') {
      this.display = val;
      this.stack.push(parseInt(this.display, 10));
    } else {
      this.display += val;
      this.stack[this.stack.length - 1] = parseInt(this.display, 10);
    }
  }

  operatorPressed(val: string) {
    const precedenceMap = {'+': 0, '-': 0, '*': 1, '/': 1};
    this.ensureNumber();
    const precedence = precedenceMap[val];
    let reduce = true;
    while (reduce) {
      let i = this.stack.length - 1;
      let lastPrecedence = 100;

      while (i >= 0) {
        if (typeof this.stack[i] === 'string') {
          lastPrecedence = precedenceMap[this.stack[i]];
          break;
        }
        i--;
      }
      if (precedence <= lastPrecedence) {
        reduce = this.reduceLast();
      } else {
        reduce = false;
      }
    }

    this.stack.push(val);
  }

  equalPressed() {
    this.ensureNumber();
    while (this.reduceLast()) {}
    this.stack.pop();
  }

  percentPressed() {
    this.ensureNumber();
    while (this.reduceLast()) {}
    const result = this.stack.pop() as number / 100;
    this.display = result.toString(10);
  }

  acPressed() {
    this.stack = ['='];
    this.display = '0';
  }

  cePressed() {
    if (typeof this.stack[this.stack.length - 1] === 'number') { this.stack.pop(); }
    this.display = '0';
  }

  private ensureNumber() {
    if (typeof this.stack[this.stack.length - 1] === 'string') {
      this.stack.push(parseInt(this.display, 10));
    }
  }

  private reduceLast() {
    if (this.stack.length < 4) { return false; }
    const num2 = this.stack.pop() as number;
    const op = this.stack.pop() as string;
    const num1 = this.stack.pop() as number;
    let result = num1;
    switch (op) {
      case '+': result = num1 + num2;
        break;
      case '-': result = num1 - num2;
        break;
      case '*': result = num1 * num2;
        break;
      case '/': result = num1 / num2;
        break;
    }
    this.stack.push(result);
    this.display = result.toString(10);
    return true;
  }
}

This code contains a complete calculator. See how the callback function for the buttons in the HTML template has been implemented as member functions. The calculator knows the four basic operations +-*, and /, and it is aware of operator precedence. I will not go into the details of how this is being achieved. I’ll leave this for you to figure out as an exercise.

Before you can start testing the application, you need to register the new components with the router. Open up src/app/app-routing.module.ts and edit its contents to match the following.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HomeComponent } from './home/home.component';
import { CalculatorComponent } from './calculator/calculator.component';

const routes: Routes = [
  { path: '', component: HomeComponent },
  { path: 'calculator', component: CalculatorComponent },
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

The demo application is now complete and you can fire up the server. The Angular command line tool comes with a development server that is ideal for testing the application. It will automatically cause the browser to reload the application whenever you make any changes to the code. To start the development server, simply run the following command.

ng serve

Open up your browser, navigate to http://localhost:4200, and click on the calculator icon in the top right corner. You should see something like the image below.

Add Authentication to Your Angular App

In this section, I will show you how to add authentication to your application. Okta provides a simple solution to secure authentication with easy integration into Angular applications. The ready-made route guard lets you restrict access to selected routes simply by dropping it into the route specification. The application flow is as follows. Whenever a user requests a protected resource, the route guard will check if the user is logged in. If the user is not logged in the guard will redirect the user to a hosted login page on the Okta servers. Alternatively, the user may opt to click on a login link directly. In this case, the authentication service will redirect the user to the login page. Once the user is logged in, the login page will redirect the user back to a special route, usually called implicit/callback, in the application. This route is managed by the Okta Callback Component. The callback component will decide where to redirect the user, depending on the original request and on the authentication status of the user.

Before you start, you will need to register a free developer account with Okta. Open your browser, navigate to developer.okta.com, and click the Create Free Account button. In the form that appears next, enter your details. Complete your registration by clicking the Get Started button.

Once you have gone through the registration process you will be taken to your Okta dashboard. Select the Applications link in the top menu. Here you can see an overview of all the applications that you have linked to your Okta account. If you are a new member, this list will be empty. To create your first application, click on the green button that says Add Application. On the screen that appears next, select Single-Page App and click Next. On the next screen, you can edit the settings. The Base URI should point to the location of your application. Since you are using the standard settings for the Angular development server, this should be set to http://localhost:4200/. The Login Redirect URI is the location that the user will be redirected back to after a successful login. This should match the callback route in your application, so you need to set it to http://localhost:4200/implicit/callback. When you’re done, click on the Done button. The resulting screen will provide you with a client ID which you need to copy and paste into your application.

_Angular app on Okta_

To start implementing authentication in your application, you need to install the Okta Angular library. Open the terminal in the application directory and run the command:

npm install @okta/[email protected]

Open src/app/app.module.ts and add the following import to the top of the file.

import { OktaAuthModule } from '@okta/okta-angular';

In the same file, in the imports section of the @NgModule annotation, add the following code.

OktaAuthModule.initAuth({
  issuer: 'https://okta.okta.com/oauth2/default',
  redirectUri: 'http://localhost:4200/implicit/callback',
  clientId: '{yourClientId}'
})

The {yourClientId} placeholder should be replaced with the client ID that you obtained in the Okta dashboard. Open src/app/app.component.ts and replace the contents of the file with the following.

import { Component, OnInit } from '@angular/core';
import { OktaAuthService } from '@okta/okta-angular';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  title = 'AngularCalculator';
  isAuthenticated: boolean;

  constructor(public oktaAuth: OktaAuthService) {
    this.oktaAuth.$authenticationState.subscribe(
      (isAuthenticated: boolean)  => this.isAuthenticated = isAuthenticated
    );
  }

  ngOnInit() {
    this.oktaAuth.isAuthenticated().then((auth) => {
      this.isAuthenticated = auth;
    });
  }

  login() {
    this.oktaAuth.loginRedirect();
  }

  logout() {
    this.oktaAuth.logout('/');
  }
}

The OktaAuthService is injected into the main application component. The main component contains a isAuthenticated flag that keeps track of the authentication status of the user. By subscribing to the $authenticationState observable, this flag is kept up to date whenever the status changes. The flag is initialized in the ngOnInit function. The login member function simply calls OktaAuthService.loginRedirect, which redirects the user to the hosted login page. Similarly, the logout member function calls OktaAuthService.logout, which erases any user tokens and redirects the user to the main route.

Next, open src/app/app.component.html and add the following code into the <mat-toolbar> after the closing tag of the first <span> element.

<span>
  <button mat-button *ngIf="!isAuthenticated" (click)="login()"> Login </button>
  <button mat-button *ngIf="isAuthenticated" (click)="logout()"> Logout </button>
</span>

By making use of the isAuthenticated flag, either a Login or a Logout button is shown. Each button calls the respective method of the application component to log the user in or out. In the final step, you need to modify the router settings. Open src/app/app-routing.module.ts and add the following import to the top of the file.

import { OktaCallbackComponent, OktaAuthGuard } from '@okta/okta-angular';

In the code above and in the Okta dashboard settings, you have specified that implicit/callback route should handle the login callback. To register the OktaCallbackComponent with this route, add the following entry to the routes setting.

{ path: 'implicit/callback', component: OktaCallbackComponent }

The OktaAuthGuard can be used to restrict access to any protected routes. To protect the calculator route, modify its entry by adding a canActivate property in the following way.

{ path: 'calculator', component: CalculatorComponent, canActivate: [OktaAuthGuard] }

Now, if you try to access the calculator in the application, you will be redirected to the Okta login page. Only on a successful login are you going to be redirected back to the calculator. The splash screen, on the other hand, is accessible without any authentication.

Angular Authentication with the Login Widget

Redirecting the user to an external login page is OK for some use cases. In other cases, you don’t want the user to leave your site. This is a use case for the login widget. It lets you embed the login form directly into your application. To make use of the widget, you first have to install it. Open the terminal in the application directory and install the following packages.

npm install @okta/[email protected] [email protected]

Next, generate a component that hosts the login form. This component will not need any additional styling, and the HTML template will consist only of a single tag that can be inlined in the component definition.

ng generate component login --inline-style=true --inline-template=true

Open the newly created src/app/login/login.component.ts and paste the following contents into it.

import { Component, OnInit } from '@angular/core';
import { Router, NavigationStart} from '@angular/router';

import { OktaAuthService } from '@okta/okta-angular';
import * as OktaSignIn from '@okta/okta-signin-widget';

@Component({
  selector: 'app-login',
  template: `<div id="okta-signin-container"></div>`,
  styles: []
})
export class LoginComponent implements OnInit {
  widget = new OktaSignIn({
    baseUrl: 'https://okta.okta.com'
  });

  constructor(private oktaAuth: OktaAuthService, router: Router) {
    // Show the widget when prompted, otherwise remove it from the DOM.
    router.events.forEach(event => {
      if (event instanceof NavigationStart) {
        switch(event.url) {
          case '/login':
          case '/calculator':
            break;
          default:
            this.widget.remove();
            break;
        }
      }
    });
  }

  ngOnInit() {
    this.widget.renderEl({
      el: '#okta-signin-container'},
      (res) => {
        if (res.status === 'SUCCESS') {
          this.oktaAuth.loginRedirect('/', { sessionToken: res.session.token });
          // Hide the widget
          this.widget.hide();
        }
      },
      (err) => {
        throw err;
      }
    );
  }
}

In src/index.html, add the following two lines inside the <head> tag to include the default widget styles.

<link href="https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.14.0/css/okta-sign-in.min.css" type="text/css" rel="stylesheet"/>
<link href="https://ok1static.oktacdn.com/assets/js/sdk/okta-signin-widget/2.14.0/css/okta-theme.css" type="text/css" rel="stylesheet"/>

Now, add the login route to the route configuration. Open src/app/app-routing.module.ts. Add an import for the LoginComponent at the top of the file.

import { LoginComponent } from './login/login.component';

Next, add a function that tells the router what to do when the user is required to log in.

export function onAuthRequired({ oktaAuth, router }) {
  router.navigate(['/login']);
}

Make sure that the function is exported. In the routes specification, add the route for the login component.

{ path: 'login', component: LoginComponent }

Finally, modify the specification for the calculator route to include a reference to the onAuthRequired function.

{
  path: 'calculator',
  component: CalculatorComponent,
  canActivate: [OktaAuthGuard],
  data: { onAuthRequired }
}

The next step is to make sure that the user is directed to the login page when the login button in the top bar is pressed. Open src/app/app.component.html and change the line containing the login button to the following.

<button mat-button *ngIf="!isAuthenticated" routerLink="/login"> Login </button>

You can also remove the login function in src/app/app.component.ts, as it is no longer needed.

That’s it! Your application now hosts its own login form powered by Okta. Below is a screenshot of what the login widget might look like.

_Angular calculator with sign-in widget_ Conclusion

In this tutorial, I showed you how to implement authentication and basic authorization in a single page application based on Angular. You have the choice between a hosted login page and a login widget embedded in your application. The hosted login is ideal when you know that there are multiple applications linked to a single user account. In this case, the hosted solution conveys the idea that the user is logging on to all applications in one central location. The login widget is the ideal solution when you want to provide a seamless experience in a single branded application.

The code for this tutorial can be found on GitHub

You may also like: Add Authentication to Your Angular PWA

Did you like this tutorial? Please share if you liked it!