Tag: Angular
Do you believe Vue.JS will surpass React.JS in 2023?
It’s difficult to predict the future of web development frameworks, but it’s unlikely that Vue will completely overtake React in popularity. React has been widely adopted and has a large community of developers, which makes it a strong contender in the web development space.
However, Vue has also gained popularity in recent years and has a growing community of developers. Vue’s simplicity and ease of use have made it a popular choice for developers who are new to web development or who prefer a simpler approach to building web applications.
Ultimately, the choice between Vue.js and React.js (or any other framework) depends on the specific needs of a project and the preferences of the development team.
Both frameworks have their strengths and weaknesses, and the decision should be based on a careful evaluation of the project requirements and available resources.
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 2023.
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 2023: 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>
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?
Best Editor for Angular.JS
We can’t boil the ocean.
8 Best Angular IDE To Use In 2020
- Angular IDE
- Webstorm
- Visual Studio Code
- Sublime Text
- Brackets
- AtomÂ
- Aptana Studio
- ALM IDE
I won’t develop it more here, feel free to Google for it and you’ll end with many relevant results.
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 | Â |