0

I'm just starting to learn Angular 2 and Firebase and have gotten stuck on a basic problem.

I'm using the Firebase createUserWithEmailAndPassword method to register a new user account using HTML form data, which is stored in a custom login class and then through the method.

This works, but I can't work out how to transfer any error data I get in the error object out to my HTML template for display. I've added variables to the login class, but I cant access it inside the createUserWithEmailAndPassword method.

I know this is probably something fundamental but any help would be really useful. Thanks!

LoginComponent.ts

import { Component } from "@angular/core";
import { Router }    from "@angular/router";

import { Login }     from "./login";

@Component({
    selector: 'login-component',  
    templateUrl: 'app/login/login.component.html',
    styleUrls:[
        'app/login/login.component.css'
    ]
})

export class LoginComponent{

    login = new Login('James', 'SuperSecret', false, '');
    submitted = false;

    onSubmit(){this.submitted= true};

    active = true;

    newUser(){
                    firebase.auth().createUserWithEmailAndPassword(this.login.email, this.login.pswd).catch(function(error) {
            if(error){
                console.log('The error code: ' + error.code + '\nThe error message' + error.message);
                this.login.error = true;
                this.login.errorMsg = error.message;
            }else{
                this.login.error=false;
                this.login.errorMsg='';
            }                
        });
    }

}

LoginComponent.html

<div class="container">
    <div [hidden]="submitted">
        <h1>Login Form</h1>
        <form *ngIf="active" (ngSubmit)="onSubmit()" #loginForm="ngForm">            

            <!--  Email Input  -->
            <div class="form-group">
                <label for="email">Email</label>
                <input type="email" class="form-control" id="email" required 
                        [(ngModel)]="login.email" name="email" #email="ngModel">
                <div [hidden]="email.valid || email.pristine"
                        class="alert alert-danger">
                        Email is required
                </div>
            </div>

            <!--  Password Input  -->
            <div class="form-group">
                <label for="pswd">Password</label>
                <input type="password" class="form-control" id="pswd" [(ngModel)]="login.pswd" name="pswd" required>

                <!--  EXAMPLE ERROR DISPLAY  -->
                <div [hidden]="!login.error" class="alert alert-danger">{{login.errorMsg}}</div>
            </div>

             <!-- Submit Buttons  -->
            <button type="submit" class="btn btn-default" [disabled]="!loginForm.form.valid">Login</button>
            <button type="button" class="btn btn-default" (click)="newUser()">Register</button>
        </form>
    </div>
</div>

Login.ts (Login class)

export class Login{
    constructor(
        public email: string,
        public pswd:  string,
        public error: boolean,
        public errorMsg: string
    ){}
}
AL.
  • 36,815
  • 10
  • 142
  • 281
aheigins
  • 2,574
  • 3
  • 19
  • 26
  • I'm able to interact with the login object if i add a .then() method after the .catch(). Why though cant i access this within the .catch anonymous function? – aheigins Sep 06 '16 at 00:04

1 Answers1

1

Your problem is not related to firebase or the promise api but some pretty generic mistake on how this works in javascript. You assume to access the LoginComponent when assigning the error but the function passed to the catch() method doesn't use the LoginComponent as this. You are most likely setting the error on the window object.

There are several options to fix this.

Binding the correct this:

newUser() {
  firebase.auth()
    .createUserWithEmailAndPassword(this.login.email, this.login.pswd)
    .catch(
      function(error) {
        this.login.errorMsg = error.message;
      }
      .bind(this)
    );
}

Using a closure:

newUser() {
  // capturing 'this' in a variable
  var self = this;
  firebase.auth()
    .createUserWithEmailAndPassword(this.login.email, this.login.pswd)
    .catch(
      function(error) {
        // using the captured this (in the self variable)
        self.login.errorMsg = error.message;
      }
    );
}

Using arrow functions (that don't have a this argument and thus are looking for this in the outer scope. This requires ES2015, so you need to transpile when targeting older browsers. As you are using TypeScript, I'd suggest to use this version because it is the most concise one.

newUser() {
  firebase.auth()
    .createUserWithEmailAndPassword(this.login.email, this.login.pswd)
    .catch(
      (error) => {
        this.login.errorMsg = error.message;
        // further assignments
      }
    );
}

When you only need to execute one action, you can even simplify this by omitting the parentheses:

newUser() {
  firebase.auth()
    .createUserWithEmailAndPassword(this.login.email, this.login.pswd)
    .catch(error => this.login.errorMsg = error.message);
}

Btw: When working with a promise based API you should be safe without checking the error for existence when you implement the catch callback as this is only called in case of an error...

For more details, please read this great answer about this: How to access the correct `this` context inside a callback?

Community
  • 1
  • 1
Andreas Jägle
  • 11,632
  • 3
  • 31
  • 31