Skip to content

Commit 678a94e

Browse files
Code at end of Part 8
1 parent ee41cef commit 678a94e

19 files changed

+278
-56
lines changed

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"license": "MIT",
88
"scripts": {
99
"ng": "ng",
10-
"start": "ng serve",
10+
"start": "ng build --prod && ng build --prod --app universal --output-hashing=none && node server.js",
1111
"build": "ng build --prod && ng build --prod --app universal --output-hashing=none",
1212
"test": "ng test",
1313
"lint": "ng lint",

src/app/app.module.ts

+12-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { BrowserModule } from '@angular/platform-browser';
22
import { FormsModule } from '@angular/forms';
3+
import { HttpClientModule } from '@angular/common/http';
34
import { NgModule } from '@angular/core';
45
import { RouterModule, Routes } from '@angular/router';
56

@@ -12,13 +13,16 @@ import { ProfileComponent } from './components/profile/profile.component';
1213
import { RegisterComponent } from './components/register/register.component';
1314

1415
import { ValidateService } from './services/validate.service';
16+
import { AuthService } from './services/auth.service';
17+
import { NotFoundComponent } from './components/not-found/not-found.component';
1518

1619
const appRoutes: Routes = [
1720
{ path: '', component: HomeComponent },
1821
{ path: 'register', component: RegisterComponent },
1922
{ path: 'login', component: LoginComponent },
2023
{ path: 'dashboard', component: DashboardComponent },
21-
{ path: 'profile', component: ProfileComponent }
24+
{ path: 'profile', component: ProfileComponent },
25+
{ path: '**', component: NotFoundComponent }
2226
];
2327

2428
@NgModule({
@@ -29,14 +33,19 @@ const appRoutes: Routes = [
2933
LoginComponent,
3034
NavbarComponent,
3135
ProfileComponent,
32-
RegisterComponent
36+
RegisterComponent,
37+
NotFoundComponent
3338
],
3439
imports: [
3540
BrowserModule.withServerTransition({ appId: 'serverApp' }),
3641
FormsModule,
42+
HttpClientModule,
3743
RouterModule.forRoot(appRoutes),
3844
],
39-
providers: [ValidateService],
45+
providers: [
46+
ValidateService,
47+
AuthService
48+
],
4049
bootstrap: [AppComponent]
4150
})
4251
export class AppModule { }
+23-21
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,26 @@
1-
<div class="jumbotron text-center">
2-
<h1 class="display-3">MEAN Authentication App</h1>
3-
<p class="lead">Welcome to our custom MEAN authentication application built from scratch</p>
4-
<hr class="my-4">
5-
<div>
6-
<a href="" class="btn btn-primary" routerLink="/register">Register</a>
7-
<a href="" class="btn btn-primary" routerLink="/login">Login</a>
1+
<div class="fadein">
2+
<div class="jumbotron text-center">
3+
<h1 class="display-3">MEAN Authentication App</h1>
4+
<p class="lead">Welcome to our custom MEAN authentication application built from scratch</p>
5+
<hr class="my-4">
6+
<div>
7+
<a href="" class="btn btn-primary" routerLink="/register">Register</a>
8+
<a href="" class="btn btn-primary" routerLink="/login">Login</a>
9+
</div>
810
</div>
9-
</div>
1011

11-
<div class="row">
12-
<div class="col-md-4">
13-
<h3>Express Backend</h3>
14-
<p>A rock solid Node.js/Express server using Mongoose to organize models and query the database.</p>
12+
<div class="row">
13+
<div class="col-md-4">
14+
<h3>Express Backend</h3>
15+
<p>A rock solid Node.js/Express server using Mongoose to organize models and query the database.</p>
16+
</div>
17+
<div class="col-md-4">
18+
<h3>Angular CLI</h3>
19+
<p>Angular CLI to generate components, services, and more. Local dev server and easy and easy compilation</p>
20+
</div>
21+
<div class="col-md-4">
22+
<h3>JWT Tokens</h3>
23+
<p>Full featured authentication using JSON web tokens. Login and store user data.</p>
24+
</div>
1525
</div>
16-
<div class="col-md-4">
17-
<h3>Angular CLI</h3>
18-
<p>Angular CLI to generate components, services, and more. Local dev server and easy and easy compilation</p>
19-
</div>
20-
<div class="col-md-4">
21-
<h3>JWT Tokens</h3>
22-
<p>Full featured authentication using JSON web tokens. Login and store user data.</p>
23-
</div>
24-
</div>
26+
</div>
+19-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,19 @@
1-
<p>
2-
login works!
3-
</p>
1+
<div class="fadein">
2+
<h2 class="page-header">Login</h2>
3+
<div class="alert alert-danger" *ngIf="errSwitch" role="alert">{{ errMsg }}</div>
4+
<form #loginForm="ngForm" (ngSubmit)="onLoginSubmit(loginForm)">
5+
<div class="form-group">
6+
<label for="username">Username
7+
<sup class="red">*</sup>
8+
</label>
9+
<input required name="username" id="username" #username="ngModel" ngModel type="email" class="form-control">
10+
</div>
11+
<div class="form-group">
12+
<label for="password">Password
13+
<sup class="red">*</sup>
14+
</label>
15+
<input required name="password" id="password" #password="ngModel" ngModel type="password" class="form-control">
16+
</div>
17+
<input type="submit" class="btn btn-primary" value="Login" [disabled]="!loginForm.valid">
18+
</form>
19+
</div>
+24-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Component, OnInit } from '@angular/core';
2+
import { AuthService } from '../../services/auth.service';
3+
import { Router } from '@angular/router';
24

35
@Component({
46
selector: 'app-login',
@@ -7,9 +9,30 @@ import { Component, OnInit } from '@angular/core';
79
})
810
export class LoginComponent implements OnInit {
911

10-
constructor() { }
12+
errSwitch = false;
13+
errMsg: string;
14+
15+
constructor(private authService: AuthService, private router: Router) { }
1116

1217
ngOnInit() {
1318
}
1419

20+
onLoginSubmit(form) {
21+
const user = {
22+
username: form.value.username.toLowerCase(),
23+
password: form.value.password
24+
};
25+
26+
this.authService.authenticateUser(user).subscribe((data: any) => {
27+
if (data.success) {
28+
this.authService.storeUserData(data.token, data.user);
29+
this.router.navigateByUrl('/dashboard');
30+
} else {
31+
this.errSwitch = true;
32+
this.errMsg = 'Invalid username/password';
33+
}
34+
});
35+
36+
}
37+
1538
}

src/app/components/navbar/navbar.component.html

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
<div class="form-inline">
99
<li><a class="btn" routerLinkActive="active" routerLink="/login">Login</a></li>
1010
<li><a class="btn" routerLinkActive="active" routerLink="/register">Register</a></li>
11+
<li><a class="btn" (click)="onLogoutClick()" href="#">Logout</a></li>
1112
</div>
1213
</div>
1314
</div>
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import { Component, OnInit } from '@angular/core';
2+
import { AuthService } from './../../services/auth.service';
3+
import { Router } from '@angular/router';
24

35
@Component({
46
selector: 'app-navbar',
@@ -7,9 +9,14 @@ import { Component, OnInit } from '@angular/core';
79
})
810
export class NavbarComponent implements OnInit {
911

10-
constructor() { }
12+
constructor(private authService: AuthService, private router: Router) { }
1113

1214
ngOnInit() {
1315
}
1416

17+
onLogoutClick() {
18+
this.authService.logout();
19+
this.router.navigateByUrl('/');
20+
}
21+
1522
}

src/app/components/not-found/not-found.component.css

Whitespace-only changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
<h1 style="text-align: center; margin-top: 20px;">Error 404: Page Not Found</h1>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2+
3+
import { NotFoundComponent } from './not-found.component';
4+
5+
describe('NotFoundComponent', () => {
6+
let component: NotFoundComponent;
7+
let fixture: ComponentFixture<NotFoundComponent>;
8+
9+
beforeEach(async(() => {
10+
TestBed.configureTestingModule({
11+
declarations: [ NotFoundComponent ]
12+
})
13+
.compileComponents();
14+
}));
15+
16+
beforeEach(() => {
17+
fixture = TestBed.createComponent(NotFoundComponent);
18+
component = fixture.componentInstance;
19+
fixture.detectChanges();
20+
});
21+
22+
it('should create', () => {
23+
expect(component).toBeTruthy();
24+
});
25+
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { Component, OnInit } from '@angular/core';
2+
3+
@Component({
4+
selector: 'app-not-found',
5+
templateUrl: './not-found.component.html',
6+
styleUrls: ['./not-found.component.css']
7+
})
8+
export class NotFoundComponent implements OnInit {
9+
10+
constructor() { }
11+
12+
ngOnInit() {
13+
}
14+
15+
}
Original file line numberDiff line numberDiff line change
@@ -1,3 +0,0 @@
1-
.red {
2-
color: red;
3-
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
1-
<h2 class="page-header">Register</h2>
2-
<div class="alert alert-danger" *ngIf="noEmail" role="alert">{{ errMsg }}</div>
3-
<form #regForm="ngForm" (ngSubmit)="onRegisterSubmit(regForm)">
4-
<div class="form-group">
5-
<label for="name">Name <sup class="red">*</sup></label>
6-
<input required name="name" id="name" #name="ngModel" ngModel type="text" class="form-control" ></div>
7-
<div class="form-group">
8-
<label for="username">Username <sup class="red">*</sup></label>
9-
<input required name="username" id="username" #username="ngModel" ngModel type="text" class="form-control"></div>
10-
<div class="form-group">
11-
<label for="email">Email <sup class="red">*</sup></label>
12-
<input required name="email" id="email" #email="ngModel" ngModel type="email" class="form-control"></div>
13-
<div class="form-group">
14-
<label for="password">Password <sup class="red">*</sup></label>
15-
<input required name="password" id="password" #password="ngModel" ngModel type="password" class="form-control"></div>
16-
<input type="submit" class="btn btn-primary" value="Submit" [disabled]="!regForm.valid">
17-
</form>
1+
<div class="fadein">
2+
<h2 class="page-header">Register</h2>
3+
<div class="alert alert-danger" *ngIf="errSwitch" role="alert">{{ errMsg }}</div>
4+
<div class="alert alert-success" *ngIf="succSwitch" role="alert">{{ succMsg }}</div>
5+
<form #regForm="ngForm" (ngSubmit)="onRegisterSubmit(regForm)">
6+
<div class="form-group">
7+
<label for="name">Name <sup class="red">*</sup></label>
8+
<input required name="name" id="name" #name="ngModel" ngModel type="text" class="form-control" ></div>
9+
<div class="form-group">
10+
<label for="username">Username <sup class="red">*</sup></label>
11+
<input required name="username" id="username" #username="ngModel" ngModel type="text" class="form-control"></div>
12+
<div class="form-group">
13+
<label for="email">Email <sup class="red">*</sup></label>
14+
<input required name="email" id="email" #email="ngModel" ngModel type="email" class="form-control"></div>
15+
<div class="form-group">
16+
<label for="password">Password <sup class="red">*</sup></label>
17+
<input required name="password" id="password" #password="ngModel" ngModel type="password" class="form-control"></div>
18+
<input type="submit" class="btn btn-primary" value="Submit" [disabled]="!regForm.valid || submitSwitch">
19+
</form>
20+
</div>
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { ValidateService } from '../../services/validate.service';
21
import { Component, OnInit } from '@angular/core';
2+
import { ValidateService } from '../../services/validate.service';
3+
import { AuthService } from '../../services/auth.service';
34

45
@Component({
56
selector: 'app-register',
@@ -8,10 +9,13 @@ import { Component, OnInit } from '@angular/core';
89
})
910
export class RegisterComponent implements OnInit {
1011

11-
noEmail = false;
12+
errSwitch = false;
13+
succSwitch = false;
14+
submitSwitch = false;
1215
errMsg: string;
16+
succMsg: string;
1317

14-
constructor(private validateService: ValidateService) { }
18+
constructor(private validateService: ValidateService, private authService: AuthService) { }
1519

1620
ngOnInit() {
1721
}
@@ -20,11 +24,27 @@ export class RegisterComponent implements OnInit {
2024

2125
// Validate Email
2226
if (!this.validateService.validateEmail(form.value.email)) {
23-
this.noEmail = true;
27+
this.errSwitch = true;
2428
this.errMsg = 'Please use a valid email';
2529
return false;
2630
}
2731

32+
// Register User
33+
this.authService.registerUser(form.value).subscribe((data: any) => {
34+
if (data.success) {
35+
this.succSwitch = true;
36+
this.succMsg = 'You are now registered and can log in';
37+
this.errSwitch = false;
38+
this.errMsg = '';
39+
this.submitSwitch = true;
40+
} else {
41+
this.succSwitch = false;
42+
this.succMsg = '';
43+
this.errSwitch = true;
44+
this.errMsg = 'Something went wrong';
45+
}
46+
});
47+
2848
}
2949

3050
}

src/app/services/auth.service.spec.ts

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { TestBed, inject } from '@angular/core/testing';
2+
3+
import { AuthService } from './auth.service';
4+
5+
describe('AuthService', () => {
6+
beforeEach(() => {
7+
TestBed.configureTestingModule({
8+
providers: [AuthService]
9+
});
10+
});
11+
12+
it('should be created', inject([AuthService], (service: AuthService) => {
13+
expect(service).toBeTruthy();
14+
}));
15+
});

src/app/services/auth.service.ts

+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { Injectable } from '@angular/core';
2+
import { HttpClient, HttpHeaders } from '@angular/common/http';
3+
4+
@Injectable()
5+
export class AuthService {
6+
7+
authToken: any;
8+
user: any;
9+
10+
constructor(private http: HttpClient) { }
11+
12+
registerUser(user) {
13+
user.username = user.username.toLowerCase();
14+
let headers = new HttpHeaders();
15+
headers.append('Content-Type', 'application/json');
16+
return this.http.post('http://localhost:3000/users/register', user, { headers });
17+
}
18+
19+
authenticateUser(user) {
20+
let headers = new HttpHeaders();
21+
headers.append('Content-Type', 'application/json');
22+
return this.http.post('http://localhost:3000/users/authenticate', user, { headers });
23+
}
24+
25+
storeUserData(token, user) {
26+
localStorage.setItem('id_token', token);
27+
localStorage.setItem('user', JSON.stringify(user));
28+
this.authToken = token;
29+
this.user = user;
30+
}
31+
32+
logout() {
33+
this.authToken = null;
34+
this.user = null;
35+
localStorage.clear();
36+
}
37+
38+
}

0 commit comments

Comments
 (0)