Twitter LogoFacebook Logo
Angular Project 1: Building a Social Media Site Part 5: Login System Part 2
Hello, welcome to another tutorial of The Complete Angular Course. The focus for this video is to finish the login system.
By: King

The focus for this video is to finish the login system. In the previous session, we added the features to create an account, login, and reset the password. Now, we will add the feature to verify the user’s email and detect when they log in or out of our app.

Adding to the Authenticator Component

To begin, we’ll add some code to Authenticator Component. Every time we successfully create an account, log in, or reset the password, the Authenticator Component still shows up. We want to remove it when any of the functions completes.

Go to the Authenticator Component TypeScript file and import the MatBottomSheetRef class from Angular Material. Then inject it in the constructor.

import { MatBottomSheetRef } from '@angular/material/bottom-sheet';

export class AuthenticatorComponent implements OnInit {
  ...

  constructor(private bottomSheetRef: MatBottomSheetRef) { 
    ...
  }

  ...
}

With the Bottom Sheet Ref object, we can close the bottom sheet. Go to the onComplete() callback function of onResetClick() and remove the code inside. 

this.firebasetsAuth.sendPasswordResetEmail(

   {
       email: email,
       onComplete: (err) => {
            
       }
    }
);

To close the bottom sheet, grab the bottom sheet ref object, and call the dismiss() method. 

this.firebasetsAuth.sendPasswordResetEmail(

   {
       email: email,
       onComplete: (err) => {
             this.bottomSheetRef.dismiss();
       }
    }
);

We’ll do the same for the other 2 functions. In the onLoginClick() and onRegisterClick() functions, remove the code in the onComplete() callback function and call the dismiss() method.

  onLogin(
    loginEmail: HTMLInputElement,
    loginPassword: HTMLInputElement
  ){
    let email = loginEmail.value;
    let password = loginPassword.value;

    if(this.isNotEmpty(email) && this.isNotEmpty(password)) {
      this.firebasetsAuth.signInWith(
        {
          email: email,
          password: password,

          onComplete: (uc) => {
            this.bottomSheetRef.dismiss();
          },

          onFail: (err) => {
            alert(err);
          }
        }
      );
    }
  }

  onRegisterClick(
    registerEmail: HTMLInputElement,
    registerPassword: HTMLInputElement,
    registerConfirmPassword: HTMLInputElement
  ){
    let email = registerEmail.value;
    let password = registerPassword.value;
    let confirmPassword = registerConfirmPassword.value;

    if(
      this.isNotEmpty(email) &&
      this.isNotEmpty(password) && 
      this.isNotEmpty(confirmPassword) &&
      this.isAMatch(password, confirmPassword)
    ){
      this.firebasetsAuth.createAccountWith(
        {
          email: email,
          password: password,
          onComplete: (uc) => {
            this.bottomSheetRef.dismiss();
          },
          onFail: (err) => {
            alert("Failed to create the account.");
          }
        }
      );
    }
  }

If we go to our app and use any of the 3 features, the bottom sheet will close if it was successful. 

If any of it fails, the bottom sheet will stay opened and we can enter the information again. 

Detecting User Logins

Now, lets detect when a user logs in or out of the site. Go to the App Component TypeScript file and import the FirebaseTSAuth class. Then create a FirebaseTSAuth object.

import { FirebaseTSAuth } from 'firebasets/firebasetsAuth/firebaseTSAuth';

export class AppComponent {
  
  auth = new FirebaseTSAuth();
  ...

}

We want to place the code to detect the login states in the App Component because it is the entry point of the app. This way, we will able to direct and control users who enters the site from the start.

The FirebaseTSAuth class, contains a function call listenToSingInStateChanges(). The function will attach a listener object to our app and it’ll listen to any changes made to the log in state.

In the constructor, call the method.

constructor(private loginSheet: MatBottomSheet){
    this.auth.listenToSignInStateChanges();
}

Inside the parenthesis, pass in the callback function for it to call when it gets triggered. 

this.auth.listenToSignInStateChanges(

   user => {

   }
);

The user argument, represents the Firebase user object from Firebase Authentication. We can use it to get some information about the user. 


If the user argument returns a value, it means a user has logged in. If it returns null, it means no user is logged in or they have logged out.

this.auth.listenToSignInStateChanges(

   user => {

      if(user){
          // Logged In
      } else  {
          // Logged Out
      }

   }
);

But instead of using the if statement, the FirebaseTSAuth class contains another method call checkSignInState() that we can call inside. 

this.auth.listenToSignInStateChanges(

   user => {
       this.auth.checkSignInState();
   }
);

The first callback function is whenSignedIn(). It’ll get triggered if a user logs into the site.

The second callback function is whenSignedOut() which will get triggered if the user logs out of the site.

The third and fourth callback functions are whenSignedInAndEmailNotVerified and whenSignedInAndEmailVerified. These functions will get triggered depending on if they user has their email verified when they log in.

And the last callback function is whenChanged() which will get triggered if any of the 4 states are triggered.

this.auth.checkSignInState(
   {
        whenSignedIn: user => {

        },
        whenSignedOut: user => {

        },
        whenSignedInAndEmailNotVerified: user => {

        },
        whenSignedInAndEmailVerified: user => {

        },
        whenChanged: user => {

        }
   }
);

Add an alert statement in the whenSignedIn and whenSignedOut states.

this.auth.checkSignInState(
   {
        whenSignedIn: user => {
             alert("Logged in");
        },
        whenSignedOut: user => {
             alert("Logged out");
        },
        ...
   }
);

If we go to the app on the browser, we’ll get a popup message that says we’re logged in. This is because we have logged in earlier and that session remains until you log out.

Add Logout Feature

Let’s add the feature to log out. 

Go back to the App Component TypeScript file and define a function call loggedIn(). This function will check if there is a user that is logged in. 

We can declare a boolean variable, set the value in the callback functions, and then return it,  but the FirebaseTSAuth class comes with a method call isSignedIn() which does just that.

loggedIn(){
    return this.auth.isSignedIn();
}

Go to the App Component HTML page. 


Add another navigation item call Logout and attach a click event to it.

<div id="nav-bar-right">

  <div (click)="onLoginClick()" class="nav-bar-items">
       Login
  </div>
  <div (click)="onLogoutClick()" class="nav-bar-items" >
       Logout
  </div>

</div>

Then use the *ngIf directive to hide or show each navigation. For the Login navigation item, we want to show it, only if we’re not logged in. For Logout, we want to show it if we are logged in.

<div *ngIf="!loggedIn()"
      (click)="onLoginClick()" class="nav-bar-items">
       Login
</div>
<div *ngIf="loggedIn()"
      (click)="onLogoutClick()" class="nav-bar-items" >
       Logout
</div>

Go to the App Component TypeScript file and define the function for the click event. To logout, grab the FirebaseTSAuth object and call the signOut() method.

onLogoutClick(){
    this.auth.signOut();
}

If we run the app again, the logout button will show since we are logged in. 

Image from Codeible.com

If we click on the logout button, the login button will show and we will get a different popup.

Image from Codeible.com

Verifying Email

Lets see how we can verify the user’s email. Go to the App Component TypeScript file and locate the whenSignedInAndEmailNotVerified callback function.

Every Firebase user has a property call emailVerified that will return true if their email is verified. 

What it means to be verified

Having  a verified email, means that the user has access to the email. We can verify this by sending a verification email to the user’s inbox and they have to click on a link that will set the emailVerified property to true.

Open the terminal and create a new component call EmailVerification. Run the ng generate component command and place it in the directory call pages.

ng generate component pages/EmailVerification

1) Go to the Email Verification Component HTML page. Remove the default code and then add a div element. Call it email-verification.


2) Inside the div element, add a header and a button element. 

<div id="email-verification">
    <h1>Your Email is Not Verified</h1>
    <button>Resend Verification Email</button>
</div>

For the button, use the mat-flat-button style and attach a click event to it. The purpose of the button is to resend the verification email to the user. 

<button

   mat-flat-button
   (click)="onResendClick()"
>Resend Verification Email</button>

Go to the Email Verification Component CSS file and add the selector for email-verification. Set text-align to center and margin-top to 8em.

#email-verification {
    text-align: center;
    margin-top: 8em;
}

Go to the TypeScript file and define the onResendClick() function for the click event.

onResendClick(){
 
}

Import the FirebaseTSAuth class and create a FirebaseTSAuth object.

Inside the onResendClick() function, grab the FirebaseTSAuth object and call the sendVerificationEmail() method. 

The method will send a verification email to the user that is logged in.

onResendClick(){
    this.auth.sendVerificationEmail();
}

Import the Router class from @angular/router and inject it inside the constructor.

import { Router } from '@angular/router';

constructor(private router: Router) { }

In the ngOnInit() function and use the FirebaseTSAuth object to check if there is a user that is logged in with their email not verified. 

If yes, send the verification email and print out a message to the user.

If not, send them back to the home page by grabbing the router object and calling the navigate() function.

We want to do this because we do not want people to just come to this page when they are not logged in or have their email verified already.

ngOnInit(): void {
    if(
      this.auth.isSignedIn() &&
      !this.auth.getAuth().currentUser.emailVerified
    ) {

      this.auth.sendVerificationEmail();

    } else {

      this.router.navigate([""]);

    }
}

Defining a Route for the Email Verification Page

Go into the App Routing Module and add a route for the Email Verification page. 


For the path, use emailVerification, and for the component, use the Email Verification Component.

const routes: Routes = [
  {path: "", component: HomeComponent},
  {path: "emailVerification", component: EmailVerificationComponent},
  {path: "**", component: HomeComponent}
];

Go to the App Component TypeScript file. Import the router class and inject it in the constructor as well.

import { Router } from '@angular/router';

constructor(
          private loginSheet: MatBottomSheet,
          private router: Router)
{

    this.auth.listenToSignInStateChanges(
      {
           ...
      }
    );

}

In the whenSignedInAndEmailNotVerified callback function, call the navigate function from the router object and go to the Email Verification page. 


Lastly, remove the alert() statements in the sign in and out callback functions.

this.auth.checkSignInState(
   {
        whenSignedIn: user => {

        },
        whenSignedOut: user => {

        },
        whenSignedInAndEmailNotVerified: user => {
             this.router.navigate(["emailVerification"]);
        },
        whenSignedInAndEmailVerified: user => {

        },
        whenChanged: user => {

        }
   }
);

If we run the app and log in, we will be directed to the Email Verification page since we have not verified our email yet.


If we go to our email inbox, we will see a no-reply email to verify our email. Click on it and then click on the link.

Image from Codeible.com

If we go back to our web site and refresh the page, we will be directed back to the home page since we have successfully verified our email.


Sign In