Getting Started | Building an Application with Angular
In this tutorial we will be creating a register/Login and Logout page. We will be using a hard coded user name and password for authenticating a user. Also will be implementing session management so that only a used who is logged in can view the pages. Else he will be directed to the login page.
Create a new authentication service where we check if the user name and password is correct then set it in session storage. Using sessionStorage properties we can save key/value pairs in a web browser. The sessionStorage object stores data for only one session . So the data gets deleted if the browser is closed.
ng new
The Angular CLI makes it easy to create an application that already works, right out of the box. It already follows our best practices!
ng generate
Generate components, routes, services and pipes with a simple command. The CLI will also create simple test shells for all of these.
ng serve
Easily test your app locally while developing.
Open Command and tap this commands
$ npm install -g @angular/cli
$ ng new register-login
$ cd register-login
$ ng serve
We need to create three components
ng g c auth/register
ng g c auth/login
ng g c auth/profile
The next step We need to create two Services
ng g s auth/service/auth
ng g s auth/service/tokenStorage
The next step We need to create three Models
Create a new folder model in the auth folder inside it create three models
RegisterInfo.ts
LoginInfo.ts
ProfileInfo.ts
Install Angular Material & Routing Module
ng add @angular/material
ng generate module app-routing --flat --module=app
app.module.ts
import {BrowserModule} from '@angular/platform-browser';
import {NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import {AppComponent} from './app.component';
import {BrowserAnimationsModule } from '@angular/platform-browser/animations';
import {MatCardModule} from '@angular/material/card';
import {MatButtonModule } from '@angular/material/button';
import {MatInputModule} from '@angular/material/input';
import {MatSelectModule } from '@angular/material/select';
import {MatIconModule} from '@angular/material/icon';
import {FormsModule, ReactiveFormsModule } from '@angular/forms';
import {MatSliderModule} from '@angular/material/slider';
import {HttpClientModule } from '@angular/common/http';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {MatListModule } from '@angular/material/list';
import {MatProgressBarModule } from'@angular/material/progress-bar';
import {MatStepperModule} from '@angular/material/stepper';
import {MatRadioModule } from '@angular/material/radio';
import {MatFormFieldModule} from '@angular/material/form-field';
import {AppRoutingModule } from './app-routing.module';
import {RegisterComponent} from './auth/register/register.component';
import {LoginComponent } from './auth/login/login.component';
import {ProfileComponent} from './auth/profile/profile.component';
@NgModule({
declarations: [
AppComponent,
ProfileComponent,
RegisterComponent,
LoginComponent
],
imports: [
BrowserModule,
BrowserAnimationsModule,
BrowserModule,
BrowserAnimationsModule,
MatCardModule,
MatButtonModule,
MatInputModule,
MatSelectModule,
MatIconModule,
ReactiveFormsModule,
FormsModule,
MatSliderModule,
HttpClientModule,
FormsModule,
MatProgressSpinnerModule,
MatListModule,
MatProgressBarModule,
MatStepperModule,
MatRadioModule,
MatFormFieldModule,
AppRoutingModule
],
schemas: [
CUSTOM_ELEMENTS_SCHEMA
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
app.routing.module.ts
import {RegisterComponent} from './auth/register/register.component';
import {NgModule } from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {LoginComponent } from './auth/login/login.component';
import {ProfileComponent} from './auth/profile/profile.component';
const routes: Routes = [
{ path: '', redirectTo: '/login', pathMatch: 'full' },
{
path: 'login',
component: LoginComponent
},
{
path: 'register',
component: RegisterComponent
},
{
path: 'profile/:username',
component: ProfileComponent
}
];
@NgModule({
imports: [RouterModule. forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
LoginInfo.ts
export class LoginInfo {
username: string;
password: string;
constructor(username: string, password: string) {
this.username = username;
this.password = password;
}
}
RegisterInfo.ts
export class RegisterInfo {
name: string;
username: string;
email: string;
password: string;
gender: string;
birthDate: any;
createAt: any;
role: string[];
constructor(name: string,
username: string,
email: string,
password: string,
gender: string,
birthDate: any,
createAt: any) {
this.name = name;
this.username = username;
this.email = email;
this.password = password;
this.gender = gender;
this.birthDate = birthDate;
this.createAt = createAt;
this.role = ['user'];
}
}
ProfileInfo.ts
export class ProfileInfo {
id: number;
name: string;
username: string;
email: string;
password: string;
gender: string;
birthDate: any;
createAt: any;
}
auth.service.ts
import {Injectable } from '@angular/core';
import {HttpClient, HttpHeaders} from '@angular/common/http';
import {Observable } from 'rxjs';
import {LoginInfo} from '../model/LoginInfo';
import {RegisterInfo } from '../model/RegisterInfo';
const httpOptions = {
headers: new HttpHeaders({'content-Type': 'application/json'})
};
@Injectable({
providedIn: 'root'
})
export class AuthService {
constructor(private http: HttpClient) {}
private urlAuth = 'http://localhost:8080/api/auth';
login(info: LoginInfo): Observable<any> {
return this.http.post<any>(this.urlAuth + '/login', info, httpOptions);
}
register(info: RegisterInfo): Observable<RegisterInfo> {
return this.http.post<RegisterInfo>(this.urlAuth + '/register', info, httpOptions);
}
getUserByUsername(username: string): Observable<any> {
return this.http.get<any>(this.urlAuth + `/getUserByUsername/${username}`);
}
findUserById(id: number): Observable<any> {
return this.http.get<any>(this.urlAuth + `/findUserById/${id}`);
}
updateUser(user: any, id: number): Observable<any> {
return this.http.put<any>(this.urlAuth + `/updateUser/${id}`, user);
}
deleteUser(id: number): any {
this.http.delete(this.urlAuth + `/deleteUser/${id}`, { responseType: 'text' });
}
findAllUsers(): Observable<RegisterInfo[]> {
return this.http.get<RegisterInfo[]>(this.urlAuth + `/findAllUsers`);
}
}
token-storage.service.ts
import {Injectable } from '@angular/core';
const TOKEN_KEY = 'AuthToken';
const USERNAME_KEY = 'AuthUsername';
const AUTHORITIES_KEY = 'AuthAuthorities';
const NAME_KEY = 'AuthName';
const EMAIL_KEY = 'AuthEmail';
@Injectable({
providedIn: 'root'
})
export class TokenStorageService {
private roles: Array<string> = [];
public signOut(): void {
window.sessionStorage.clear();
}
public saveToken(token: string): void {
window.sessionStorage.removeItem(TOKEN_KEY);
window.sessionStorage.setItem(TOKEN_KEY, token);
}
public getToken(): string {
return sessionStorage.getItem(TOKEN_KEY);
}
public saveUsername(username: string): void {
window.sessionStorage.removeItem(USERNAME_KEY);
window.sessionStorage.setItem(USERNAME_KEY, username);
}
public getUsername(): string {
return sessionStorage.getItem(USERNAME_KEY);
}
public saveName(name: string): void {
window.sessionStorage.removeItem(NAME_KEY);
window.sessionStorage.setItem(NAME_KEY, name);
}
public getName(): string {
return sessionStorage.getItem(NAME_KEY);
}
public saveEmail(email: string): void {
window.sessionStorage.removeItem(EMAIL_KEY);
window.sessionStorage.setItem(EMAIL_KEY, email);
}
public getEmail(): string {
return sessionStorage.getItem(EMAIL_KEY);
}
public saveAuthorities(authorities: string[]): void {
window.sessionStorage.removeItem(AUTHORITIES_KEY);
window.sessionStorage.setItem(AUTHORITIES_KEY, JSON.stringify(authorities));
}
public getAuthorities(): string[] {
this.roles = [];
if (sessionStorage.getItem(TOKEN_KEY)) {
JSON.parse(sessionStorage.getItem(AUTHORITIES_KEY)).forEach(authority => {
this.roles. push(authority.authority);
});
}
return this.roles;
}
}
register.component.ts
import {Component, OnInit} from '@angular/core';
import {Router } from '@angular/router';
import {LoginInfo} from '../model/LoginInfo';
import {RegisterInfo } from '../model/RegisterInfo';
import {AuthService} from '../service/auth.service';
import {TokenStorageService } from '../service/token-storage.service';
@Component({
selector: 'app-register',
templateUrl: './register.component.html',
styleUrls: ['./register.component.css']
})
export class RegisterComponent implements OnInit {
form: any = {};
isSignedUp = false;
signUpInfo: RegisterInfo;
showSpinner = false;
isSignUpFailed = false;
errorMessage = false;
gender: any = {};
roles: string[] = [];
loginInfo: LoginInfo;
profileInfo: RegisterInfo[];
constructor(private authService: AuthService, private router: Router,
private tokenStorageService: TokenStorageService) {
}
ngOnInit(): void {
this.gender = 'female';
}
setGender(): void {
this.form.gender = this.gender;
}
onSubmit(): void {
this.showSpinner = true;
this.signUpInfo = new RegisterInfo(
this.form.name,
this.form.username,
this.form.email,
this.form.password,
this.form.gender,
this.form.birthDate,
this.form.createdAt,
);
this.signUpInfo.role = ['admin'];
this.authService.register(this.signUpInfo).subscribe(signUpInfo => {
this.signUpInfo = signUpInfo;
this.loginInfo = new LoginInfo(
this.form.username,
this.form.password
);
this.authService.login(this.loginInfo).subscribe(loginInfo => {
this.tokenStorageService.saveToken(loginInfo.accessToken);
this.tokenStorageService.saveUsername(loginInfo.username);
this.tokenStorageService.saveAuthorities(loginInfo.authorities);
this.roles = this.tokenStorageService.getAuthorities();
this.isSignedUp = true;
this.showSpinner = false;
this.isSignedUp = true;
this.isSignUpFailed = false;
this.router.navigate(['/profile/' + this.form.username]);
});
}, error => {
this.errorMessage = error.error.message;
this.isSignUpFailed = true;
});
}
reset(): void {
this.form.name = '';
this.form.username = '';
this.form.email = '';
this.form.password = '';
this.form.gender = '';
this.form.birthDate = '';
this.form.createAt = '';
}
}
register.component.html
<div class="content">
<br>
<mat-card class="example-card" style="background: blanchedalmond;">
<mat-card-header style="background: #d35400; padding: 15px 10px 6px; color: #000fff; border-radius: 5px">
<mat-card-title>
<mat-icon>how_to_reg</mat-icon>
<span style="margin-left: 8px">Register</span>
</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-list>
<mat-list-item>Please fill the fields</mat-list-item>
<mat-divider></mat-divider>
</mat-list>
<mat-progress-bar mode="indeterminate" *ngIf="showSpinner">
</mat-progress-bar>
<form (ngSubmit)="formRegister.form.valid && onSubmit() " #formRegister="ngForm" novalidate>
<mat-vertical-stepper linear style="background: blanchedalmond;">
<mat-step label="Step 1">
<mat-form-field>
<mat-label>Username</mat-label>
<input type="text" matInput placeholder="username" name="username" #username="ngModel" [(ngModel)]="form.username">
</mat-form-field> <br>
<mat-form-field>
<mat-label>Email</mat-label>
<input type="text" matInput placeholder="email" name="email" #email="ngModel" [(ngModel)]="form.email">
</mat-form-field> <br>
<mat-form-field>
<mat-label>Password</mat-label>
<input type="password" matInput placeholder="password" name="password" #password="ngModel" [(ngModel)]="form.password">
</mat-form-field> <br>
</mat-step>
<mat-step label="Step 2">
<mat-form-field>
<mat-label>Name</mat-label>
<input type="text" matInput placeholder="name" name="name" #name="ngModel" [(ngModel)]="form.name">
</mat-form-field> <br>
<mat-form-field >
<mat-label>Birth date</mat-label>
<input type="date" matInput placeholder="Birth Date" name="birthDate" #birthDate="ngModel" [(ngModel)]="form.birthDate">
</mat-form-field> <br>
<div style="margin-bottom: 10px;">
<strong>Gender</strong>
</div>
<mat-radio-group aria-label="Select an option" [(ngModel)]="gender" name="gender" (ngModelChange)="setGender()">
<mat-radio-button value="female">Female</mat-radio-button>
<mat-radio-button value="male" style="margin-left: 10px;">Male</mat-radio-button>
</mat-radio-group>
<div style="margin-top: 15px">
<button mat-raised-button color="accent" type="submit"
[disabled]="username.invalid || email.invalid || password.invalid || name.invalid || birthDate.invalid" >
Register <mat-icon>supervisor_account</mat-icon>
</button>
<button mat-button matStepperPrevious>Back </button>
<button mat-button (click)="reset()">Reset </button>
</div>
</mat-step>
</mat-vertical-stepper>
</form>
</mat-card-content> <hr>
<mat-card-actions>
<p style="margin-left: 10px; float: right">All ready i have an account
<button mat-button color="primary" routerLink="/login">
Login<mat-icon>exit_to_app</mat-icon>
</button>
</p>
</mat-card-actions>
</mat-card>
</div>
register.component.css
.content {
display: flex;
margin: 82px auto 32px;
padding: 0 16px;
max-width: 960px;
flex-direction: column;
align-items: center;
}
mat-card {
width: 600px;
}
mat-form-field {
width: 300px;
}
.alert {
padding: 4px 20px;
background-color: #1abc9c;
color: white;
border-radius: 6px;
margin-top: 30px;
}
login.component.ts
import {Component, OnInit } from '@angular/core';
import {Router } from '@angular/router';
import {LoginInfo } from '../model/LoginInfo';
import {AuthService } from '../service/auth.service';
import {TokenStorageService } from '../service/token-storage.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
form: any = {};
isLoggedIn = false;
isLoginFailed = false;
loginInfo: LoginInfo;
showSpinner = false;
constructor(private authService: AuthService, private router: Router,
private tokenStorageService: TokenStorageService) {
}
ngOnInit(): void {
}
onSubmit(): void {
this.showSpinner = true;
this.loginInfo = new LoginInfo(
this.form.username,
this.form.password
);
this.authService.login(this.loginInfo).subscribe(data => {
this.tokenStorageService.saveToken(data.accessToken);
this.tokenStorageService.saveUsername(data.username);
this.tokenStorageService.saveAuthorities(data.authorities);
this.isLoggedIn = true;
this.isLoginFailed = false;
this.router.navigate(['/profile/' + this.form.username]);
}, error => {
this.isLoginFailed = true;
this.showSpinner = false;
});
}
}
login.component.html
<div *ngIf='isLoggedIn; else loggedOut' >
<app-home></app-home>
</div>
<ng-template #loggedOut>
<div class="content">
<div *ngIf="isLoginFailed" class="alert">
<p> Username or password not correct </p>
</div>
<mat-card style="background: blanchedalmond;">
<mat-card-header>
<mat-card-title>Login</mat-card-title>
<mat-card-subtitle>Please fill the fields</mat-card-subtitle>
</mat-card-header>
<mat-card-content>
<mat-spinner [diameter]="34" *ngIf="showSpinner"></mat-spinner>
<form (ngSubmit)="formLogin.form.valid && onSubmit() " #formLogin="ngForm" novalidate>
<mat-form-field>
<mat-label>Username</mat-label>
<input type="text" matInput placeholder="username" name="username" #username="ngModel" [(ngModel)]="form.username">
</mat-form-field> <br>
<mat-form-field>
<mat-label>Password</mat-label>
<input type="password" matInput placeholder="password" name="password" #password="ngModel" [(ngModel)]="form.password">
</mat-form-field> <br>
<button mat-raised-button color="accent" type="submit" > Login<mat-icon>exit_to_app</mat-icon> </button>
</form>
</mat-card-content>
<mat-card-actions>
<p style="margin-left: 10px">I don't have an account
<button mat-button color="primary" routerLink="/register">Register <mat-icon> supervisor_account </mat-icon> </button>
</p>
</mat-card-actions>
</mat-card>
</div>
</ng-template>
login.component.css
.content {
display: flex;
margin: 82px auto 32px;
padding: 0 16px;
max-width: 960px;
flex-direction: column;
align-items: center;
}
mat-card {
width: 320px;
}
mat-form-field {
width: 300px;
}
.alert {
padding: 4px 20px;
background-color: #c0392b;
color: white;
border-radius: 6px;
margin-top: 20px;
}
profile.component.ts
import {RegisterInfo } from './../model/RegisterInfo';
import {Component, OnInit } from '@angular/core';
import {ActivatedRoute, Router } from '@angular/router';
import {AuthService } from '../service/auth.service';
import {TokenStorageService } from '../service/token-storage.service';
import {ProfileInfo } from '../model/ProfileInfo';
@Component({
selector: 'app-profile',
templateUrl: './profile.component.html',
styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
username = '';
profileInfo: ProfileInfo = {} as ProfileInfo;
constructor(private authService: AuthService, private router: Router, private route: ActivatedRoute,
private tokenService: TokenStorageService) {
}
ngOnInit(): void {
this.username = this.route.snapshot.params.username;
this.authService.getUserByUsername(this.username).subscribe(info => {
this.profileInfo = info;
console.log(this.profileInfo);
});
}
logout(): void {
this.tokenService.signOut();
this.router.navigateByUrl('/');
}
}
profile.component.html
<div class="content">
<mat-card>
<mat-card-header style="background: #d35400; padding: 15px 10px 6px; color: #000fff; border-radius: 5px">
<mat-card-title> <mat-icon>supervisor_account</mat-icon> Your profile</mat-card-title>
</mat-card-header>
<mat-card-content>
<mat-list>
<mat-list-item>Username: {{profileInfo.username}} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Email: {{profileInfo.email}} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Name: {{profileInfo.name}} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Birth date: {{profileInfo.birthDate | date}} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Gender: {{profileInfo.gender}} </mat-list-item>
<mat-divider></mat-divider>
<mat-list-item>Created At: {{profileInfo.createAt | date}} </mat-list-item>
<mat-divider></mat-divider>
</mat-list>
</mat-card-content>
<mat-card-actions>
<button mat-raised-button color="accent" (click)="logout()">Logout <mat-icon>arrow_forward</mat-icon></button>
</mat-card-actions>
</mat-card>
</div>
profile.component.css
.content {
display: flex;
margin: 82px auto 32px;
padding: 0 16px;
max-width: 960px;
flex-direction: column;
align-items: center;
}
Now Run Application
ng serve
Conclusion
Now we have an overview of Spring Boot CRUD example when building a CRUD App and Config, JWT for secuirty
We also take a look at client-server architecture for REST API using Spring Web MVC & Spring Data JPA, as well, we are gooing to continue with Angular 10 project structure for building a front-end app to make HTTP requests and consume responses.