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.
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.
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.
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
.
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.
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