Learn Angular 9 with Tailwind CSS by building a banking app - Lesson 2: Login form logic

Duomly - May 25 '20 - - Dev Community

This article was originally published at https://www.blog.duomly.com/angular-course-building-a-banking-application-with-tailwind-css-lesson-2-login-form/


A few days ago, we published the first lesson of the Angular Course, where we are building frontend for the banking application you could see in the SQL injection tutorial.

In the first lesson we started with setting up the project using Angular CLI, implementing TailwindCSS and creating a UI for the login form.

Today I’d like to go one step further and create logic for the component we’ve created. So, if you were creating the application with me in the previous part, then let’s open the code and start the app. If not, check out the Angular Course - Lesson 1: Start the project or check out our GitHub and get the code from there.

And of course, as always, we prepare you a video on our Youtube channel if you are more into watching tutorials than just reading them.

On the playlist, you’ll also find a lesson one and other lessons of this course which we will prepare for you.

There’s one more thing, on the backend, I’ll be using the app which you can find in our Golang course, so you can create and use it as well.

Let’s crush Angular!

1. Create logic for login component - handling inputs and validation

Let’s start our existing project using ng serve and let’s open login.component.ts file. I’d like to start by creating a function, which will get and save the values from the inputs to be able to use them with the submit function.

Let’s first define two values username and password, and let’s assign an empty string to those values.

username: string = '';
password: string = '';

Now, we will create an onKey() function:

onKey(event: any, type: string) {
    if (type === 'username') {
        this.username = event.target.value;
    } else if (type === 'password') {
        this.password = event.target.value;
    }
}

Right now, we need to create a validation for the username filed to prevent SQL injection. My friend showed you how to hack application putting SQL command in the input, and we would like to avoid it so, we won’t allow to send username if it’s different than only letters and numbers.

Let’s create another value isUsernameValid: boolean = true; just under the password: string = '';.

isUsernameValid: boolean = true;

Now, let’s create a validateUsername() function.

validateUsername(): void {
    const pattern = RegExp(/^[\w-.]*$/);
    if (pattern.test(this.username)) {
        this.isUsernameValid = true;    
    } else {
        this.isUsernameValid = false;
    }
}

And to make it work, let’s place this function in the onKey() function.

onKey(event: any, type: string) {
    if (type === username) {
        this.username = event.target.value;
        this.validateUsername();
    } else if (type === password) {
        this.password = event.target.value;
    }
}

The last point in this step is to implement our onKey function in the template and display the error message if username is not valid. Let’s open login.component.html and find the inputs.

<form class="bg-white shadow-md rounded px-8 pt-6 pb-8 mb-4">
  <img class="logo" src="../../assets/logo.png" />
  <div class="mb-4">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="username">
      Username
    </label>
    <input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 leading-tight focus:outline-none focus:shadow-outline" id="username" type="text" placeholder="Username" (keyup)="onKey($event, 'username')" required>
    <p *ngIf="!isUsernameValid" class="text-red-500 text-xs italic">Your username can consist of letters and numbers only!</p>
  </div>
  <div class="mb-6">
    <label class="block text-gray-700 text-sm font-bold mb-2" for="password">
      Password
    </label>
    <input class="shadow appearance-none border rounded w-full py-2 px-3 text-gray-700 mb-3 leading-tight focus:outline-none focus:shadow-outline" id="password" type="password" placeholder="******************" (keyup)="onKey($event, 'password')" required minlength="6">
  </div>
  <div class="flex items-center justify-between">
    <button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="button" (click)="onSubmit()">
      Sign In
    </button>
    <a class="inline-block align-baseline font-bold text-sm text-blue-500 hover:text-blue-800" href="#">
      Forgot Password?
    </a>
  </div>
</form>

Great, now we are ready to create a service and make an API call!

2. How to create service in Angular?

Let’s use Angular CLI to create login.service.ts. For this, we need the following command:

ng generate service services/login/login

Let’s open the created service and start from importing needed packages.

import { HttpClient, HttpHeaders } from '@angular/common/http';
import { BehaviorSubject } from 'rxjs';

Now, let’s define a contstant called httpOptions, which will be a part of our API request.

const httpOptions = {
  headers: new HttpHeaders({
    'Access-Control-Allow-Origin': '*',
  })
};

The next step is define our url and pass HttpClient to the constructor.

  url: any = <your_api_url>;

  constructor(
    private http: HttpClient,
  ) { }

Great, now we can start creating a login function. Inside the function, we need to handle the response. If we will get a token, it means that the user is logged in. We are going to save this data to the session storage to keep the user logged in only during the session.

login(Username: string, Password: string): any {
    this.http.post(this.url, { Username, Password }, httpOptions).toPromise().then((res: any) => {
      if (res && res.jwt) {
        sessionStorage.setItem('jwt', res.jwt);
      }
    });
  }

Besides that, we need to handle an error message if the login call will return it. For this, we’d use behaviourSubject from rxjs. Let’s implement it!

errorSubject: any = new BehaviorSubject<any>(null);
errorMessage: any = this.errorSubject.asObservable();

...

login(Username: string, Password: string): any {
    this.http.post(this.url, { Username, Password }, httpOptions).toPromise().then((res: any) => {
      if (res && res.jwt) {
        sessionStorage.setItem('jwt', res.jwt);
        this.errorSubject.next(null);
      } else if (res.Message) {
        this.errorSubject.next(res.Message);
      }
    });
  }

Let's import Router from @angular/router, to redirect user after login to the Dashboard view.

import { Router } from '@angular/router';
...
constructor(
  private http: HttpClient,
  private router: Router,
) { }

...
login(Username: string, Password: string): any {
  this.http.post(this.ulr, { Username, Password }, httpOptions).toPromise().then((res: any) => {
    if (res && res.jwt) {
      sessionStorage.setItem('jwt', res.jwt);
      this.errorSubject.next(null);
      this.router.navigateByUrl('dashboard');
    } else if (res.Message) {
      this.errorSubject.next(res.Message);
    }
  });
}

Before we make it work together we can't forget about adding HttpClientModule to app.module.ts file. So, let's open it and make sure your code looks like to one below.

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

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';

@NgModule({
  declarations: [
    AppComponent,
    LoginComponent,
    DashboardComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    HttpClientModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

Fine, now we can go back to login.component.ts file, and let’s create an onSubmit() function using LoginService. Of course, we have to import the service and set it in the constructor.

import { LoginService } from './../services/login/login.service';

...

constructor(
  private loginService: LoginService,
) { }

onSubmit() {
  if (this.isUsernameValid) {
    this.loginService
    .login(this.username, this.password);
  }
}

The next this we would like to add here is error value where we will be listening if error didn’t appear. We will subscribe to error message from LoginService in ngOnInit() method.

  error: any = null;
...

  ngOnInit(): void {
    this.loginService
      .errorMessage
      .subscribe(errorMessage => {
        this.error = errorMessage;
      });
  }

And we are almost there, we just need to switch to the login.component.html file and add values to our template.

First let’s assign onSubmit() function to our Login button.

<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded focus:outline-none focus:shadow-outline" type="button" (click)="onSubmit()">
  Sign In
</button>

And next let’s fill in missing values in the notification element. Also let’s define when the element will be visible.

<div *ngIf="error" class="notification bg-indigo-900 text-center py-4 lg:px-4">
    <div class="p-2 bg-indigo-800 items-center text-indigo-100 leading-none lg:rounded-full flex lg:inline-flex" role="alert">
      <span class="flex rounded-full bg-indigo-500 uppercase px-2 py-1 text-xs font-bold mr-3">ERROR</span>
      <span class="font-semibold mr-2 text-left flex-auto">{{error}}</span>
    </div>
</div>

And voila. Login almost works! Now it’s time to work on guard which will prevent not logged users from entering the application.

3. How to create a guard in Angular routing?

So, at the beginning let’s create a guard file using Angular CLI.

ng generate guard services/guards/auth-guard

Now, let’s our login.service.ts file, where we are going to create one more function isAuthenticated().

  isAuthenticated() {
    if (sessionStorage.getItem('jwt')) {
      return true;
    } else {
      return false;
    }
  }

Okey, we can switch to our auth-guard.service.ts file. In the beginning let’s import LoginService, Router and CanActivate.

import { Router, CanActivate } from '@angular/router';
import { LoginService } from './login/login.service';

Next we can set LoginService and Router in the constructor and then let’s create canActiveate() function which will return true if the user is authenticated and will redirect us to the dashboard component and return false if a user is not authenticated.

@Injectable()
export class AuthGuardService implements CanActivate {
  constructor(
    public login: LoginService,
    public router: Router) { }

  canActivate(): boolean {
    if (!this.login.isAuthenticated()) {
      this.router.navigateByUrl('/');
      return false;
    }
    return true;
  }
}

4. Creating a dashboard component

Let’s use Angular CLI to create a dashboard component.

ng generate component dashboard

Now, let’s open the dashboard.component.ts file and we will create a simple headline there.

<div id="dashboard-component" class="container mx-auto grid">
  <div class="flex mb-4 mt-24">
    <div class="mx-auto">
      <h3 class="text-gray-700 font-normal mx-auto">
        Hi User,here is your dashboard:
      </h3>
    </div>
  </div>
</div>

Also, let’s add one line of custom styles in our dashboard.component.scss file.

#dashboard-component {
  h3 {
    font-size: 24px;
  }
}

As the last step, let’s add a route for a dashboard component. Please open the app-routing.module.ts file and create a new route.
In this route we are going to use the AuthGuard, so we need to import it as well.

import { DashboardComponent } from './dashboard/dashboard.component';
import { AuthGuardService as AuthGuard } from './services/auth-guard.service';

const routes: Routes = [
  { path: '', component: LoginComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
];

Now, it’s time to test it!

Conclusion

I hope you were able to create the code for lesson one and lesson two and you have a running login for our application.

Remember to take a look at the Golang course where you can build a backend for this application, so you will have a fully running project. If you don’t have any backend you can easily mock the data.

In the next lessons we are going to get users data to display on the dashboard and create other interesting features.

Stay tuned and let us know in the comments if you like the course!
If you got lost in the code, here’s a GitHub repository with lesson two so you can compare the code and find the bugs.

Angular Course - Lesson Two - Github Code

Thank you for reading,
Anna from Duomly

Duomly - Programming Online Courses

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player