Blaze to Angular 2 Migration
https://github.com/dotansimha/angular2-blaze-migration-tutorial
Migrating Authentication Templates
Note: If you skipped ahead to this section, click here to download a zip of the tutorial at this point.So now we will take care of the authentication Blaze Templates, such as Join and Signup.
We already created a stub Angular 2 Components for them - we just need to implement them now.
This Todos project uses AccountTemplates package, which has a default style templates for signin and join pages - we do not want to use those and we want to implement it with Angular 2.
The style and template defined in imports/ui/accounts/accounts-templates.html and we will copy the thing we need and create a new Angular 2 template file that looks the same.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<div class="page auth"> <nav> <div class="nav-group"> <a href="#" class="js-menu nav-item"> <span class="icon-list-unordered"></span> </a> </div> </nav> <div class="content-scrollable"> <div class="wrapper-auth"> <div class="at-form"> <h1 class="title-auth">Join</h1> <p class="subtitle-auth">Signing in allows you to have private lists</p> <div class="at-pwd-form"> <form role="form" id="at-pwd-form"> </form> </div> </div> </div> </div></div>So this is the basic layout without the actual form fields, let's use it:
1
2
3
4
5
6
7
import {Component} from "@angular/core";@Component({ templateUrl: '/client/imports/components/join.html'})export class JoinComponent {}Now let's add the actual form:
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
</a> </div> </nav> <div class="content-scrollable"> <div class="wrapper-auth"> <div class="at-form"> <h1 class="title-auth">Join</h1> <p class="subtitle-auth">Signing in allows you to have private lists</p> <div class="list-errors"> <div *ngFor="let errorText of errors" class="list-item">{{errorText}}</div> </div> <div class="at-pwd-form"> <form id="at-pwd-form" (ngSubmit)="join()" #joinForm="ngForm"> <div class="input"> <input [(ngModel)]="model.email" required type="text" id="email" name="email" placeholder="Email" class="form-control" autocapitalize="none" autocorrect="off"> </div> <div class="input"> <input [(ngModel)]="model.password" required type="password" id="password" name="password" class="form-control" placeholder="Password" autocapitalize="none" autocorrect="off"> </div> <div class="input"> <input [(ngModel)]="model.passwordVerify" required type="password" id="password_verify" class="form-control" name="password_verify" placeholder="Password (Again)" autocapitalize="none" autocorrect="off"> </div> <button type="submit" class="btn-primary" [disabled]="!joinForm.form.valid">REGISTER
</button> </form> </div> </div> </div> </div></div>Let's understand what do we have here:
- A form, that registers an event
ngSubmitto the Component methodjoin, and we give it a namejoinFormusing variable reference (more info here) - 3 inputs for email, password and verify password, that declared as
ngControlwhich indicate that this input related to the form and effect it's validation. - We also use two-way binding using
ngModelfor the inputs. - Button of type
submitthat disabled when the form is not valid.
Great, now we need to add some code to the form:
- Handle errors using
errorsarray. - Implement
join()method and create the actual user when join. - Create a model object with our fields (email, password, verifyPassword) - note that this is optional and you can just use regular object.
- Use router to navigate the user to the main page after joining.
So let's do it:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import {Component, NgZone} from "@angular/core";import {Router} from "@angular/router";class JoinModel { constructor(public email : string, public password : string, public passwordVerify : string) { }}@Component({ templateUrl: '/client/imports/components/join.html'})export class JoinComponent { private model : JoinModel; private errors : Array<string> = []; constructor(private router : Router, private zone: NgZone) { this.model = new JoinModel('', '', ''); } resetErrors() { this.errors = []; } join() { this.resetErrors(); if (this.model.password !== this.model.passwordVerify) { this.errors.push("Passwords does not match!"); return; } Accounts.createUser({ email: this.model.email, password: this.model.password}, (err) => {
if (err) { this.zone.run(() => { this.errors.push(err.reason); }); return; } this.router.navigate(['/']); }); }}And we also need to add an import for Angular 2 Forms Module, so let's do it:
7
8
9
10
11
12
13
28
29
30
31
32
33
34
35
import {Angular2BlazeTemplateModule} from "angular2-blaze-template";import {JoinComponent} from "./components/join.component";import {SigninComponent} from "./components/signin.component";import {FormsModule} from "@angular/forms";@NgModule({ // Components, Pipes, Directive...some lines skipped...imports: [
BrowserModule,
routing,
Angular2BlazeTemplateModule,
FormsModule
],
// Main Componentbootstrap: [MainComponent]
This Todo base project uses packages that intent to help developing Blaze Template with Meteor Accounts, and we no longer need it, and it is also "takes control" of sign-up, so we need to remove it.
So let's remove those packages, by running:
meteor remove useraccounts:unstyled useraccounts:flow-routing softwarerero:accounts-t9n
And we also perform some cleanup and remove some files that uses this packages - you can see those modifications in commit #7.6 (or here)
Great! now we need to make sure that there is an indication for the user that he's logged in, so let's go back to MainContainerComponent and and add currentUser field:
16
17
18
19
20
21
22
33
34
35
36
37
38
39
40
private menuOpen : boolean = false; private userMenuOpen : boolean = false; private lists: Observable<any>; private currentUser : Meteor.User; constructor(private router: Router) { this.isCordova = Meteor.isCordova;...some lines skipped...{userId: Meteor.userId()},
]
}).zone(); this.currentUser = Meteor.user(); }); }We put that code inside
autorunbecause we want it to update when the user login or logout.
Now we should be able to see the user's name if the main page - the only missing thing is to fix and add toggle for the user menu:
1
2
3
4
5
6
7
<div id="container" [ngClass]="{'menu-open': menuOpen, 'cordova': isCordova}"> <section id="menu"> <div *ngIf="currentUser" class="btns-group-vertical"> <a class="js-user-menu btn-secondary" (click)="userMenuOpen = !userMenuOpen"> <span *ngIf="userMenuOpen" class="icon-arrow-up"></span> <span *ngIf="!userMenuOpen" class="icon-arrow-down"></span>Now, let's do the same for the SigninComponent - it's very similar:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<div class="page auth"> <nav> <div class="nav-group"> <a href="#" class="js-menu nav-item"> <span class="icon-list-unordered"></span> </a> </div> </nav> <div class="content-scrollable"> <div class="wrapper-auth"> <div class="at-form"> <h1 class="title-auth">Signin</h1> <p class="subtitle-auth">Signing in allows you to have private lists</p> <div class="list-errors"> <div *ngFor="let errorText of errors" class="list-item">{{errorText}}</div> </div> <div class="at-pwd-form"> <form id="at-pwd-form" (ngSubmit)="join()" #joinForm="ngForm"> <div class="input"> <input [(ngModel)]="model.email" required type="text" id="email" name="email" placeholder="Email" class="form-control" autocapitalize="none" autocorrect="off"> </div> <div class="input"> <input [(ngModel)]="model.password" required type="password" id="password" name="password" class="form-control" placeholder="Password" autocapitalize="none" autocorrect="off"> </div> <button type="submit" class="btn-primary" [disabled]="!joinForm.form.valid">SIGN IN
</button> </form> </div> </div> </div> </div></div>And the Component:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import {Component, NgZone} from "@angular/core";import {Router} from "@angular/router";class SigninModel { constructor(public email : string, public password : string) { }}@Component({ templateUrl: '/client/imports/components/signin.html'})export class SigninComponent { private model : SigninModel; private errors : Array<string> = []; constructor(private router: Router, private ngZone: NgZone) { this.model = new SigninModel('', ''); } resetErrors() { this.errors = []; } join() { this.resetErrors(); Meteor.loginWithPassword(this.model.email, this.model.password, (err) => { if (err) { this.ngZone.run(() => { this.errors.push(err.reason); }); return; } this.router.navigate(['/']); }); }}That's it! we implemented the join/signin forms with Angular 2 !

