Angular
Building RealWorld, Production-Quality Apps with Angular

How To: Login and Register Pages

PRO

I finished! On to the next chapter

Outline

Throughout this tutorial series we'll be creating a handful of forms. To start off, lets create the forms (and pages) for logging in and registering for Conduit. We'll start by creating the HTML template for an "AuthComponent" that handles both registering and logging in.

Create the template for the AuthComponent

src/app/auth/auth.component.html

<div class="auth-page">
  <div class="container page">
    <div class="row">

      <div class="col-md-6 offset-md-3 col-xs-12">
        <h1 class="text-xs-center">{{ title }}</h1>
        <p class="text-xs-center">
          <a [routerLink]="['/login']" *ngIf="authType == 'register'">Have an account?</a>
          <a [routerLink]="['/register']" *ngIf="authType == 'login'">Need an account?</a>
        </p>

        <form [formGroup]="authForm" (ngSubmit)="submitForm()">
          <fieldset [disabled]="isSubmitting">
            <fieldset class="form-group">
              <input
                formControlName="username"
                placeholder="Username"
                class="form-control form-control-lg"
                type="text"
                *ngIf="authType == 'register'" />
            </fieldset>
            <fieldset class="form-group">
              <input
                formControlName="email"
                placeholder="Email"
                class="form-control form-control-lg"
                type="text" />
            </fieldset>
            <fieldset class="form-group">
              <input
                formControlName="password"
                placeholder="Password"
                class="form-control form-control-lg"
                type="password" />
            </fieldset>
            <button class="btn btn-lg btn-primary pull-xs-right" type="submit">
              {{ title }}
            </button>
          </fieldset>
        </form>
      </div>

    </div>
  </div>
</div>

Our login and register form are basically the same except for the username field. To keep our code DRY we take advantage of *ngIf by setting it to *ngIf = "authType == 'register' ". The username fieldset will only show when our authType variable is equal to "register".

Our component will need to expose this variable that informs the template whether they're currently on the login or register page.

Create the AuthComponent

src/app/auth/auth.component.ts

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { ActivatedRoute } from '@angular/router';

@Component({
  selector: 'auth-page',
  templateUrl: './auth.component.html'
})
export class AuthComponent implements OnInit {
  authType: String = '';
  title: String = '';
  isSubmitting: boolean = false;
  authForm: FormGroup;

  constructor(
    private route: ActivatedRoute,
    private fb: FormBuilder
  ) {
    // use FormBuilder to create a form group
    this.authForm = this.fb.group({
      'email': ['', Validators.required],
      'password': ['', Validators.required]
    });
  }

  ngOnInit() {
    this.route.url.subscribe(data => {
      // Get the last piece of the URL (it's either 'login' or 'register')
      this.authType = data[data.length - 1].path;
      // Set a title for the page accordingly
      this.title = (this.authType === 'login') ? 'Sign In' : 'Sign Up';
      // add form control for username if this is the register page
      if (this.authType === 'register') {
        this.authForm.addControl('username', new FormControl('', Validators.required));
      }
    });
  }

  submitForm() {
    this.isSubmitting = true;

    let credentials = this.authForm.value;
    // check out what you get!
    console.log(credentials);
  }
}

Lets break down what's going on here. Under our class declaration we declare our variables that are strongly typed. Lets focus on authType and title, which are set by ngOnInit. In this function authType is determined by the URL route via ActivatedRoute through the variable route. Our title and form type is also set based on authType.

Our form is taking advantage of Angular's FormBuilder. FormBuilder is syntactic sugar that makes it easier to manage form data. Here we are setting the authForm as a FormBuilder group with an email and password field. If the authType is register, then we'll add a username field (in ngOnInit). Managing our form data in this manner makes it easy to extract the data and set validation. For example in submitForm() we can set credentials to be our form's values by this.authForm.value.

Next we'll create the AuthModule which defines the routes for login and register.

Create the AuthModule

src/app/auth/auth.module.ts

import { ModuleWithProviders, NgModule } from '@angular/core';
import { RouterModule } from '@angular/router';
import { SharedModule } from '../shared';

import { AuthComponent } from './auth.component';


const authRouting: ModuleWithProviders = RouterModule.forChild([
  {
    path: 'login',
    component: AuthComponent
  },
  {
    path: 'register',
    component: AuthComponent
  }
]);

@NgModule({
  imports: [
    authRouting,
    SharedModule
  ],
  declarations: [
    AuthComponent
  ],

  providers: []
})
export class AuthModule {}

We set our child navigation and AuthComponent for the login and register paths . Since we're taking advantage of *ngIf that is set by our componet we can use AuthComponent for both routes.

Lets make it available to our app by importing our AuthComponent in our AppModule.

Import the AuthModule into the AppModule

src/app/app.module.ts



[...]

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

 import { AppComponent } from './app.component';
+import { AuthModule } from './auth/auth.module';
 import { HomeModule } from './home/home.module';
 import {
   FooterComponent,


[...]

   ],
   imports: [
     BrowserModule,
+    AuthModule,
     HomeModule,
     rootRouting,
     SharedModule


[...]

And you can now navigate to either /login or /register and see the relevant output of the AuthComponent! When you try filling out the form, the contents of the form will show up in your console (for now).

Pro tip: Console logging outputs is a great way to understand the data and the data structure you're working with. Logging data from this.route.url.subscribe will help you understand how we extracted our route information to set authType and title.

To make it easier to navigate to these pages, lets add those two links to the HeaderComponent.

Add links to the login and register pages in the HeaderComponent

src/app/shared/layout/header.component.html



[...]

       </li>

       <li class="nav-item">
+        <a class="nav-link"
+          routerLink="/login"
+          routerLinkActive="active">
           Sign in
         </a>
       </li>

       <li class="nav-item">
+        <a class="nav-link"
+          routerLink="/register"
+          routerLinkActive="active">
           Sign up
         </a>
       </li>


[...]

And we're ready to rock & roll! routerLinkActive lets us add a CSS class to an active link.

You can compare your code to the working code on Github or checkout the branch locally:

git checkout m-7