Do you believe Vue.JS will surpass React.JS in 2022?

Nope I don’t.

I’ve worked with both Vue and React. Vue.JS is my favorite so far. But, talking about surpassing react and within 2018! That’s not possible practically.

Why?

Cause even if react does something pretty bad, it’ll still take time to fade away and that’s not as quick as end of 2018. A lot of big names are using react in production. It’ll be hard to beat.

On another side, VueJS needs to do something amazing to steal everyone’s attention and make them switch from react/angular.

But, even if it becomes the best front end framework, it still needs a big name as backer so that people can trust and make decision. For example – React has Facebook, Angular has Google as backer. So, this will be a big jump if Vue can manage that.

the end, Vue.JS is a great tool. But, It’s not gonna surpass ReactJS considering the real market in 2022.

Al-Amin Nowshad, JS Developer

Vue vs React.JS Statistics Comparison

  • We know of 280,379 live websites using Vue.
  • 6th most popular in the Top 10k sites in JavaScript Library category.

Choosing Between Vue.js and ReactJS in 2021: What’s Best for Your Project?

How to build an Angular App with Authentication in 30 Minutes

Angular version 2.0 or later is a handy and yet powerful tool for creating single-page apps. There are many fascinating examples of web apps built on Angular. The most adorable feature of Angular is building reusable components, that allow you to separate different concerns of an app. Let’s take an example of authentication that we are going to create. Though it might be tough to build at the very beginning, once it’s ready, the authentication logic can be used again and again in any component of your app.

Also read: Angular Marble Testing Article with examples.

How to Build a simple Web App using Angular CLI

Now we are going to build a simple web app using Angular CLI (Command line tools). This tool allows to easily scaffold the components and even complete projects. We’ll develop an app containing search, edit and authentication features.

When it comes to custom web application development, Angular is an essential tool that saves your time, as you can reuse your code across all the platforms.

Angular App Creation Process

This task requires:

  • not more than 30 minutes
  • any text editor you prefer or any IDE, for example VS Code
  • Node.js installed
  • npm installed
  • Angular CLI installed. If you don’t have one, here is a tip: npm install -g @angular/cli

Now create a new ng-demo project and run npm install in it. Usually this task doesn’t take much time, but depends on your connection speed. Here is a command:

ng new ng-demo

[mraible:~/dev] $ ng new ng-demo
installing ng
create .editorconfig
create README.md
create src/app/app.component.css
create src/app/app.component.html
create src/app/app.component.spec.ts
create src/app/app.component.ts
create src/app/app.module.ts
create src/assets/.gitkeep
create src/environments/environment.prod.ts
create src/environments/environment.ts
create src/favicon.ico
create src/index.html
create src/main.ts
create src/polyfills.ts
create src/styles.css
create src/test.ts
create src/tsconfig.app.json
create src/tsconfig.spec.json
create src/typings.d.ts
create .angular-cli.json
create e2e/app.e2e-spec.ts
create e2e/app.po.ts
create e2e/tsconfig.e2e.json
create .gitignore
create karma.conf.js
create package.json
create protractor.conf.js
create tsconfig.json
create tslint.json
Successfully initialized git.
Installing packages for tooling via npm.
Installed packages for tooling via npm.
You can 'ng set --global packageManager=yarn'.
Project 'ng-demo' successfully created.
[mraible:~] 46s

Now when the project is created, let’s move to the next step.

Run your App

The project is served via webpack dev server. Open the ng-demo directory and run the ng serve.
You’ll see a http://localhost:4200 screen like this:

To ensure your project passed the test, run:

$ ng test
...
Chrome 60.0.3112 (Mac OS X 10.12.6): Executed 3 of 3 SUCCESS (0.239 secs / 0.213 secs)

Adding a search feature

Now let’s add an easy search feature. To do it, open the IDE, you’ve chosen or a simple text editor. If you are using IntelliJ IDEA like we do, open File > New Project > Static Web and point to the ng-demo directory.

Change directory into your project’s one and run the next command that will create a search component we are looking for:

$ ng g component search
installing component
  create src/app/search/search.component.css
  create src/app/search/search.component.html
  create src/app/search/search.component.spec.ts
  create src/app/search/search.component.ts
  update src/app/app.module.ts

Find the src/app/search/search.component.html and replace the default HTML with the following piece:

<h2>Search</h2>
<form>
  <input type="search" name="query" [(ngModel)]="query" (keyup.enter)="search()">
  <button type="button" (click)="search()">Search</button>
</form>
<pre>{{searchResults | json}}</pre>

To make your Search component work properly, you need to add the appRoutes constant into src/app/app.module.ts and import it in @NgModule:

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

const appRoutes: Routes = [
  {path: 'search', component: SearchComponent},
  {path: '', redirectTo: '/search', pathMatch: 'full'}
];

@NgModule({
  ...
  imports: [
    ...
    RouterModule.forRoot(appRoutes)
  ]
  ...
})
export class AppModule { }

In order to have the route setup, find the src/app/app.component.html, adjust the placeholder content. Then add a <router-outlet> tag to have the routes displayed.

<h1>Welcome to {{title}}!</h1>
<!-- Routed views go here -->
<router-outlet></router-outlet>

The result will look like this:

Now when you know how to create a new component in a basic Angular app using Angular CLI, let’s move to creating a localStorage to build a fake API.

To make your app get the search results, you have to create a SearchService, which is making HTTP requests to a JSON file. Begin with creating a new service:

$ ng g service search
installing service
  create src/app/search.service.spec.ts
  create src/app/search.service.ts

WARNING Service is generated but not provided, it must be provided to be used

After the service is created, you have to test how it works. That means you need to make a new directory:

mkdir -p src/app/shared/search
mv src/app/search.service.* src/app/shared/search/.

Than build src/assets/data/people.json to store your data.

[
  {
    "id": 1,
    "name": "Peyton Manning",
    "phone": "(303) 567-8910",
    "address": {
      "street": "1234 Main Street",
      "city": "Greenwood Village",
      "state": "CO",
      "zip": "80111"
    }
  },
  {
    "id": 2,
    "name": "Demaryius Thomas",
    "phone": "(720) 213-9876",
    "address": {
      "street": "5555 Marion Street",
      "city": "Denver",
      "state": "CO",
      "zip": "80202"
    }
  },
  {
    "id": 3,
    "name": "Von Miller",
    "phone": "(917) 323-2333",
    "address": {
      "street": "14 Mountain Way",
      "city": "Vail",
      "state": "CO",
      "zip": "81657"
    }
  }
]

Change the src/app/shared/search/search.service.ts and add Httpt to be a dependency in its constructor. Then create a getAll() method to collect all the people and don’t forget to determine Person and Address methods because JSON will be ranged to them.

import { Injectable } from '@angular/core';
import { Http, Response } from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class SearchService {
  constructor(private http: Http) {}

  getAll() {
    return this.http.get('assets/data/people.json')
        .map((res: Response) => res.json());
  }
}

export class Address {
  street: string;
  city: string;
  state: string;
  zip: string;

  constructor(obj?: any) {
    this.street = obj && obj.street || null;
    this.city = obj && obj.city || null;
    this.state = obj && obj.state || null;
    this.zip = obj && obj.zip || null;
  }
}

export class Person {
  id: number;
  name: string;
  phone: string;
  address: Address;

  constructor(obj?: any) {
    this.id = obj && Number(obj.id) || null;
    this.name = obj && obj.name || null;
    this.phone = obj && obj.phone || null;
    this.address = obj && obj.address || null;
  }
}

The next step will be editing src/app/shared/index.ts in order to make those classes usable for our components.

export * from './search/search.service';

Creating this file allows you to import multiple classes on a single line instead of importing each individual class on a separate line.

Find the src/app/search/search.component.ts and import the classes:

import { Person, SearchService } from '../shared';

Here you can add query and searchResults and inject the SearchService into the constructor.

export class SearchComponent implements OnInit {
  query: string;
  searchResults: Array<Person>;

  constructor(private searchService: SearchService) {}

Now implement a search() method to call the getAll() one.

search(): void {
  this.searchService.getAll().subscribe(
    data => { this.searchResults = data; },
    error => console.log(error)
  );
}

After adding the needed methods, your browser will show you the notification that you lack the provider for SearchService. To get rid of this error, you need to update the app.module.ts and import the SearchService. Don’t forget to add this service to the list of providers and import the HttpModule.

import { SearchService } from './shared';
import { HttpModule } from '@angular/http';

@NgModule({
  ...
  imports: [
    ...
    HttpModule
  ],
  providers: [SearchService],
  bootstrap: [AppComponent]
})

Now the search button works! Congratulations!

Now let’s make our results look better. To do it, open the src/app/search/search.component.html and remove the <pre> tag to replace it with a <table>.

<table *ngIf="searchResults">
  <thead>
  <tr>
    <th>Name</th>
    <th>Phone</th>
    <th>Address</th>
  </tr>
  </thead>
  <tbody>
  <tr *ngFor="let person of searchResults; let i=index">
    <td>{{person.name}}</td>
    <td>{{person.phone}}</td>
    <td>{{person.address.street}}<br/>
      {{person.address.city}}, {{person.address.state}} {{person.address.zip}}
    </td>
  </tr>
  </tbody>
</table>

Now let’s add some CSS in src/app/search/search.component.css.

table {
  margin-top: 10px;
  border-collapse: collapse;
}

th {
  text-align: left;
  border-bottom: 2px solid #ddd;
  padding: 8px;
}

td {
  border-top: 1px solid #ddd;
  padding: 8px;
}

The result we got looks much better now:

Thought it looks fine, we are still missing the search functionality! To fix it, just add the search() method to SearchService.

import { Observable } from 'rxjs';

search(q: string): Observable<any> {
  if (!q || q === '*') {
    q = '';
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data => data.filter(item => JSON.stringify(item).toLowerCase().includes(q)));
}

You also need to refactor SearchComponent to make it call this method with its query variable.

search(): void {
  this.searchService.search(this.query).subscribe(
    data => { this.searchResults = data; },
    error => console.log(error)
  );
}

After refactoring the new search result will be filtered depending on the query value you set.

And now let’s move to the next section where we’ll learn how to edit and save the records.

Add an Edit Feature

Open the src/app/search/search.component.html and add the possibility to edit the person:
<td><a [routerLink]="['/edit', person.id]">{{person.name}}</a></td>

Next, generate an Edit Components by running the following command and add the route for it in src/app/app.module.ts:

$ ng g component edit
installing component
  create src/app/edit/edit.component.css
  create src/app/edit/edit.component.html
  create src/app/edit/edit.component.spec.ts
  create src/app/edit/edit.component.ts
  update src/app/app.module.ts


const appRoutes: Routes = [
  {path: 'search', component: SearchComponent},
  {path: 'edit/:id', component: EditComponent},
  {path: '', redirectTo: '/search', pathMatch: 'full'}
];

Now, to display an editable form, you need to update src/app/edit/edit.component.html. You can also add the id attributes to most of the elements to simplify the integration tests later.

<div *ngIf="person">
  <h3>{{editName}}</h3>
  <div>
    <label>Id:</label>
    {{person.id}}
  </div>
  <div>
    <label>Name:</label>
    <input [(ngModel)]="editName" name="name" id="name" placeholder="name"/>
  </div>
  <div>
    <label>Phone:</label>
    <input [(ngModel)]="editPhone" name="phone" id="phone" placeholder="Phone"/>
  </div>
  <fieldset>
    <legend>Address:</legend>
    <address>
      <input [(ngModel)]="editAddress.street" id="street"><br/>
      <input [(ngModel)]="editAddress.city" id="city">,
      <input [(ngModel)]="editAddress.state" id="state" size="2">
      <input [(ngModel)]="editAddress.zip" id="zip" size="5">
    </address>
  </fieldset>
  <button (click)="save()" id="save">Save</button>
  <button (click)="cancel()" id="cancel">Cancel</button>
</div>

Now, import model and service classes by modifying EditComponent. This allows you to use SearchService to get data.

import { Component, OnInit, OnDestroy } from '@angular/core';
import { Address, Person, SearchService } from '../shared';
import { Subscription } from 'rxjs';
import { ActivatedRoute, Router } from '@angular/router';

@Component({
  selector: 'app-edit',
  templateUrl: './edit.component.html',
  styleUrls: ['./edit.component.css']
})
export class EditComponent implements OnInit, OnDestroy {
  person: Person;
  editName: string;
  editPhone: string;
  editAddress: Address;

  sub: Subscription;

  constructor(private route: ActivatedRoute,
              private router: Router,
              private service: SearchService) {
  }

  ngOnInit() {
    this.sub = this.route.params.subscribe(params => {
      const id = + params['id']; // (+) converts string 'id' to a number
      this.service.get(id).subscribe(person => {
        if (person) {
          this.editName = person.name;
          this.editPhone = person.phone;
          this.editAddress = person.address;
          this.person = person;
        } else {
          this.gotoList();
        }
      });
    });
  }

  ngOnDestroy() {
    this.sub.unsubscribe();
  }

  cancel() {
    this.router.navigate(['/search']);
  }

  save() {
    this.person.name = this.editName;
    this.person.phone = this.editPhone;
    this.person.address = this.editAddress;
    this.service.save(this.person);
    this.gotoList();
  }

  gotoList() {
    if (this.person) {
      this.router.navigate(['/search', {term: this.person.name} ]);
    } else {
      this.router.navigate(['/search']);
    }
  }
}

To add the possibility to find people by their ID we need to modify the search service. In order to be aware of any updated objects in local storage, you need to modify the search method.

search(q: string): Observable<any> {
  if (!q || q === '*') {
    q = '';
  } else {
    q = q.toLowerCase();
  }
  return this.getAll().map(data => {
    const results: any = [];
    data.map(item => {
      // check for item in localStorage
      if (localStorage['person' + item.id]) {
        item = JSON.parse(localStorage['person' + item.id]);
      }
      if (JSON.stringify(item).toLowerCase().includes(q)) {
        results.push(item);
      }
    });
    return results;
  });
}

get(id: number) {
  return this.getAll().map(all => {
    if (localStorage['person' + id]) {
      return JSON.parse(localStorage['person' + id]);
    }
    return all.find(e => e.id === id);
  });
}

save(person: Person) {
  localStorage['person' + person.id] = JSON.stringify(person);
}

After all the manipulations you get the search feature and the possibility to update the user’s information.

Updating the person’s data depends on the save function in src/app/edit/edit.component.html. This function calls a gotoList() and adds the person’s name to the URL, when users goes back to the search screen.

gotoList() {
  if (this.person) {
    this.router.navigate(['/search', {term: this.person.name} ]);
  } else {
    this.router.navigate(['/search']);
  }
}

To make the Search Component execute the search automatically, we need to add this logic to our constructor:

import { ActivatedRoute } from '@angular/router';
import { Subscription } from 'rxjs';
...

sub: Subscription;

constructor(private searchService: SearchService, private route: ActivatedRoute) {
  this.sub = this.route.params.subscribe(params => {
    if (params['term']) {
      this.query = decodeURIComponent(params['term']);
      this.search();
    }
  });
}

If you need to clean up the subscription, add the OnDestroy and define the ngOnDestroy method.

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

export class SearchComponent implements OnInit, OnDestroy {
...
  ngOnDestroy() {
    this.sub.unsubscribe();
  }
}

One more step complete and you have the search, edit and update functionality within your app.

Validation Form

When creating any form, the main goal is to collect any sort of information and that’s why we need to make some of the fields to be required. If you miss this step, there would be nothing to search. In our case, let’s make the Name field required. To do that we need to modify the edit.component.html by adding the required attribute to the name <input>.
<input [(ngModel)]="editName" name="name" id="name" placeholder="name" required/>

Another required action is to cover everything in a <form> element by adding <form> after the <h3> tag and before the last </div>. Then add the (ngSubmit) handler to your form, that will turn the save button into a regular submit button.

<h3>{{editName}}</h3>
<form (ngSubmit)="save()" ngNativeValidate>
  ...
  <button type="submit" id="save">Save</button>
  <button (click)="cancel()" id="cancel">Cancel</button>
</form>

Now, wherever you put the required attribute, the field becomes required.

Now the name field id required but we face another error when the address fields are blank.

If ngModel is used within a form tag, either the name attribute must be set or the form control must be defined as standalone in ngModelOptions.

Example 1: <input [(ngModel)]="person.firstName" name="first">
Example 2: <input [(ngModel)]="person.firstName" [ngModelOptions]="{standalone: true}">

Fix this error by simple adding a name attribute to all the blank fields.

  <input [(ngModel)]="editAddress.street" name="street" id="street"><br/>
  <input [(ngModel)]="editAddress.city" name="city" id="city">,
  <input [(ngModel)]="editAddress.state" name="state" id="state" size="2">
  <input [(ngModel)]="editAddress.zip" name="zip" id="zip" size="5">
</address>

Here is the fixed form:

Create an OpenID Connect App in Okta

Now we are going to use OpenID Connect (OIDC) to verify the identity of users and to get their basic profile information. Than we’ll integrate the user authentication function to our app using Okta.

The first step is registration and creation of an OIDC application.
Then, login or create a new Okta account. Click on Applications – Add application – SPA – Next.

On the last page set http://localhost:4200 as a base URI, login redirect URI and logout redirect URI. Click DONE.
Your page should look like this:

Next, add OAuth 2 and OpenID Connect using npm: npm install --save angular-oauth2-oidc

Import OAuthService into src/app/app.component.ts and make your app use the settings of your Okta app.

import { OAuthService, JwksValidationHandler } from 'angular-oauth2-oidc';

...

  constructor(private oauthService: OAuthService) {
    this.oauthService.redirectUri = window.location.origin;
    this.oauthService.clientId = '{client-id}';
    this.oauthService.scope = 'openid profile email';
    this.oauthService.issuer = 'https://dev-{dev-id}.oktapreview.com';
    this.oauthService.tokenValidationHandler = new JwksValidationHandler();

    // Load Discovery Document and then try to login the user
    this.oauthService.loadDiscoveryDocument().then(() => {
      this.oauthService.tryLogin();
    });
  }
...

Create src/app/home/home.component.ts and add the Login and Logout buttons.

import { Component } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';

@Component({
  template: `
    <div *ngIf="givenName">
      <h2>Welcome, {{givenName}}!</h2>
      <button (click)="logout()">Logout</button>
      <p><a routerLink="/search" routerLinkActive="active">Search</a></p>
    </div>

    <div *ngIf="!givenName">
      <button (click)="login()">Login</button>
    </div>`
})
export class HomeComponent {

  constructor(private oauthService: OAuthService) {
  }

  login() {
    this.oauthService.initImplicitFlow();
  }

  logout() {
    this.oauthService.logOut();
  }

  get givenName() {
    const claims = this.oauthService.getIdentityClaims();
    if (!claims) {
      return null;
    }
    return claims['name'];
  }
}

Create src/app/shared/auth/auth.guard.service.ts so the users who are not authenticated can navigate to the HomeComponent.

import { Injectable } from '@angular/core';
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { OAuthService } from 'angular-oauth2-oidc';

@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private oauthService: OAuthService, private router: Router) {}

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
    if (this.oauthService.hasValidIdToken()) {
      return true;
    }

    this.router.navigate(['/home']);
    return false;
  }
}

In src/shared/index.ts export the AuthGuard: export * from ./auth/auth.guard.service;

Then insert the OAuthModule into src/app/app.module.ts, configure the new HomeComponent, and lock the search and edit routes down using AuthGuard.

import { OAuthModule } from 'angular-oauth2-oidc';
import { HomeComponent } from './home/home.component';
import { SearchService, AuthGuard } from './shared';

const appRoutes: Routes = [
  {path: 'search', component: SearchComponent, canActivate: [AuthGuard]},
  {path: 'edit/:id', component: EditComponent, canActivate: [AuthGuard]},
  {path: 'home', component: HomeComponent},
  {path: '', redirectTo: 'home', pathMatch: 'full'},
  {path: '**', redirectTo: 'home'}
];

@NgModule({
  declarations: [
    ...
    HomeComponent
  ],
  imports: [
    ...
    OAuthModule.forRoot()
  ],
  providers: [
    AuthGuard,
    SearchService
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

These manipulations will let you to run the ng serve and see the new login button.

You can log in using some of the user’s information of your Okta App.

Authentication with the Okta Auth SDK

Finally we pass to the last milestone of our tiny app!
We’ll use the Okta’s authentication API and OAuth 2.0 API. Use npm to install:
npm install @okta/okta-auth-js –save

You also need to add a reference to this library’s main JavaScript file in .angular-cli.json:

"scripts": [
  "../node_modules/@okta/okta-auth-js/dist/okta-auth-js.min.js"
],

Now install Bootstrap 4 by: npm install bootstrap@4.0.0-beta --save
Add a reference to Bootstrap’s CSS file (src/styles.css ):
@import "~bootstrap/dist/css/bootstrap.css";

Use the Bootstrap classes for navigation bar and grid system in a src/app/app.component.html.

<nav class="navbar navbar-light bg-secondary">
  <a class="navbar-brand text-light" href="#">Welcome to {{title}}!</a>
</nav>
<div class="container-fluid">
  <router-outlet></router-outlet>
</div>

Let’s wrap the Okta Auth SDK by creating src/app/shared/auth/okta.auth.wrapper.ts and integrating it with OAuth Service.

import { OAuthService } from 'angular-oauth2-oidc';
import { Injectable } from '@angular/core';

declare const OktaAuth: any;

@Injectable()
export class OktaAuthWrapper {

  private authClient: any;

  constructor(private oauthService: OAuthService) {
    this.authClient = new OktaAuth({
      url: this.oauthService.issuer
    });
  }

  login(username: string, password: string): Promise<any> {
    return this.oauthService.createAndSaveNonce().then(nonce => {
      return this.authClient.signIn({
        username: username,
        password: password
      }).then((response) => {
        if (response.status === 'SUCCESS') {
          return this.authClient.token.getWithoutPrompt({
            clientId: this.oauthService.clientId,
            responseType: ['id_token', 'token'],
            scopes: ['openid', 'profile', 'email'],
            sessionToken: response.sessionToken,
            nonce: nonce,
            redirectUri: window.location.origin
          })
            .then((tokens) => {
              const idToken = tokens[0].idToken;
              const accessToken = tokens[1].accessToken;
              const keyValuePair = '#id_token=${encodeURIComponent(idToken)}&access_token=${encodeURIComponent(accessToken)}';
              return this.oauthService.tryLogin({ <1>
                customHashFragment: keyValuePair,
                disableOAuth2StateCheck: true
              });
            });
        } else {
          return Promise.reject('We cannot handle the ' + response.status + ' status');
        }
      });
    });
  }
}

We also need to export OktaAuthWrapper in src/shared/index.ts:
export * from './auth/okta.auth.wrapper';

And later add OktaAuthWrapper as a provider in app.module.ts.:

import {SearchService, AuthGuard, OktaAuthWrapper} from './shared';

@NgModule({
  ...
  providers: [
    ...
    OktaAuthWrapper
  ],
  bootstrap: [AppComponent]
})

In a HomeComponent modify the template of OktaAuth to have both login and sign-in buttons.

@Component({
  template: `
    <div *ngIf="givenName" class="col-12 mt-2">
      <button (click)="logout()" class="btn btn-sm btn-outline-primary float-right">Logout</button>
      <h2>Welcome, {{givenName}}!</h2>
      <p><a routerLink="/search" routerLinkActive="active">Search</a></p>
    </div>

    <div class="card mt-2" *ngIf="!givenName">
      <div class="card-body">
        <h4 class="card-title">Login with Authorization Server</h4>
        <button class="btn btn-primary" (click)="login()">Login</button>
      </div>
    </div>

    <div class="card mt-2" *ngIf="!givenName">
      <div class="card-body">
        <h4 class="card-title">Login with Username/Password</h4>

        <p class="alert alert-error" *ngIf="loginFailed">
          Login wasn't successful.
        </p>

        <div class="form-group">
          <label>Username</label>
          <input class="form-control" [(ngModel)]="username">
        </div>
        <div class="form-group">
          <label>Password</label>
          <input class="form-control" type="password" [(ngModel)]="password">
        </div>
        <div class="form-group">
          <button class="btn btn-primary" (click)="loginWithPassword()">Login</button>
        </div>
      </div>
    </div>`
})

That’s how the HomeComponent should look like:

Then comes adding the local variables for both username and password fields, importing OktaAuthWrapper, and then implementing a loginWithPassword() method in HomeComponent.

import { OktaAuthWrapper } from '../shared';
...

username;
password;

constructor(private oauthService: OAuthService,
            private oktaAuthWrapper: OktaAuthWrapper) {
}

loginWithPassword() {
  this.oktaAuthWrapper.login(this.username, this.password)
    .then(_ => console.debug('logged in'))
    .catch(err => console.error('error logging in', err));
}

Now your app has not only a sign-in form but also the possibility to search and view user’s information! Congrats!

So, guys, did you track the time for creating your small apps? How long did it take to build an Angular App with authentication?

TOP 10 US companies using Angular: Best Examples

Domain/URL Organic search
Ahrefs ratings
  Referring domains  
Referring
 
Linked
 
Backlinks
 
Target IP Keywords Traffic DR AR   Total Dofollow .gov .edu   IPs Subnets   Domains   Total Text Nofollow Redirect Image Frame Form .gov .edu  
tripadvisor.com 199.102.233.33 59,542,636 142,341,526 93 93   310,797 287,677 221 1,404   148,910 62,238   32,457   234,947,139 230,954,711 45,207,476 249,031 87,510,658 3,582,539 12,448 42,460 928,486  
capitalone.com

 

650,947 13,132,259 83 6,506   16,049 12,862 21 166   14,287 9,566   91   4,846,699 4,895,190 222,713 5,376 264,707 0 3,768 112 697  
nbcsports.com 54.204.5.8 6,400,142 8,362,730 85 4,394   66,797 59,220 15 268   38,694 21,003   2,202   56,808,853 56,345,831 17,158,003 730,718 8,332,411 0 355 87 2,752  
cvs.com

 

3,338,081 8,193,991 82 8,163   25,193 21,255 33 245   16,750 10,517   68   2,252,719 2,209,524 193,098 3,965 203,468 0 33,962 133 845  
choicehotels.com 23.215.98.194 1,573,339 2,364,321 88 1,864   36,983 32,486 116 934   25,171 16,063   121   2,673,204 2,614,307 263,321 54,944 847,161 0 15,917 1,276 8,301  
cartwheel.target.com 151.101.38.187 16,765 129,262 90 846   5,643 4,300 1 2   3,846 2,479   0   789,144 791,974 197,800 454 74,326 0 0 23 7  
jobs.officedepot.com 143.204.181.57 9,805 22,297 83 6,492   232 133 1 7   327 288   15   90,322 70,371 2,208 19,945 7,058 0 2 12 45  
privatebank.citibank.com 23.43.114.215 13,431 14,069 81 9,230   1,016 680 1 5   832 690   31   144,450 144,445 946 30 576 0 0 8 14  
opposingviews.com 151.101.194.98 49,421 11,871 73 44,582   17,929 15,118 1 65   11,920 8,058   24,675   3,441,365 3,441,184 91,329 526 23,755 0 32 1 415  
forums-es.bestbuy.com 208.74.207.24 1,904 3,881 89 1,810   40 25 0 1   41 41   82   141 140 31 1 2 0 0 0 1  
conviva.com 104.198.99.79 3,131 2,532 61 224,715   915 671 0 2   1,010 847   187   20,920 13,376 2,392 11 2,793 7,533 0 0 12  
shop.collegehumor.com 54.210.223.18 502 1,280 82 7,745   25 6 0 0   25 24   6   86 86 33 0 0 0 0 0 0  
shop.brobible.com 34.232.241.225 210 67 73 47,775   29 15 0 0   39 36   7   143 108 51 35 5 0 0 0 0  
survey.bizrate.com 2.16.122.17 6 32 83 6,605   17 10 0 0   22 22   0   67 64 33 3 1 0 0 0 0  
mycostcoaccount.costco.com

 

0 0 83 5,735   4 2 0 0   4 4   0   6 6 3 0 0 0 0 0 0