typescript - TypeError: Kan inte läsa egenskapen '_router' av null i NativeScript


Jag arbetar på en Android-app med NativeScript+AngularJS2+TypeScript. Jag följer deras dokumentation - http://docs.nativescript.org/angular/tutorial/ng-chapter-0 .

Körde fast vid en punkt när användaren efter en lyckad inloggning behöver omdirigeras till List-sidan. Följde deras Routing-avsnitt nu - http://docs.nativescript.org/angular/tutorial/ng-chapter-3#35-routing

Gjorde allt enligt deras dokument. Här är mina koder -

app.component.ts

import {ListPage} from "./pages/list/list.component";
import {Component} from "@angular/core";
import {RouteConfig} from "@angular/router-deprecated";
import {NS_ROUTER_DIRECTIVES, NS_ROUTER_PROVIDERS} from "nativescript-angular/router";
import {LoginPage} from "./pages/login/login.component";

@Component({
  selector: "main",
  directives: [NS_ROUTER_DIRECTIVES],
  providers: [NS_ROUTER_PROVIDERS],
  template: "<page-router-outlet></page-router-outlet>"
})
@RouteConfig([
  { path: "/Login", component: LoginPage, name: "Login", useAsDefault: true },
  { path: "/List", component: ListPage, name: "List" }
])
export class AppComponent {}

user.service.ts

import {Injectable} from "@angular/core";
import {User} from "./user";
import {ConfigService} from "../config";
import firebase = require("nativescript-plugin-firebase");

@Injectable()
export class UserService {

    register(user: User, config: ConfigService) {

        console.log('Email entered : '+user.email);
        console.log('Password entered : '+user.password);
        console.log("API url going to use is : "+config.apiUrl);

        firebase.init(<any>{
            persist: true, // Allow disk persistence. Default false.
            url: config.apiUrl
        }).then(
            function (instance) {
                console.log("firebase.init done");
            },
            function (error) {
                console.log("firebase.init error: " + error);
            }
        );

        return firebase.createUser({
            email: user.email,
            password: user.password
          }).then(
              function (response) {
                console.log(response);
                return response;
              }
          )

    }

    login(user: User, config: ConfigService) {

        firebase.init({
            persist: true, // Allow disk persistence. Default false.
            url: config.apiUrl
        }).then(
            function (instance) {
                console.log("firebase.init done");
            },
            function (error) {
                console.log("firebase.init error: " + error);
            }
        );

        return firebase.login({
            type: firebase.LoginType.PASSWORD,
            email: '[email ',
            password: 'firebase'
          }).then(
              function (response) {
                console.log(response);
                return response;
              }
          )

    }

}

login.component.ts

import {Router} from "@angular/router-deprecated";
import {Component} from "@angular/core";
import {User} from "../../shared/user/user";
import {UserService} from "../../shared/user/user.service";
import {ConfigService} from "../../shared/config";
import {HTTP_PROVIDERS} from "@angular/http";
import firebase = require("nativescript-plugin-firebase");

@Component({
    selector: "my-app",
    providers: [UserService, HTTP_PROVIDERS,ConfigService],
    templateUrl: "pages/login/login.html",
    styleUrls: ["pages/login/login-common.css", "pages/login/login.css"]
})

export class LoginPage {
    user: User;
    isLoggingIn = true;
    config:any;

    constructor(private _router: Router, private _userService: UserService) {
        this.user = new User();
        this.config = new ConfigService();
    }

    submit() {
        if (this.isLoggingIn) {
            this.login();
        } else {
            this.signUp();
        }
    }


    login() {

        var result = this._userService.login(this.user,this.config);

        result.then(
            function (result) {
                console.log("Login Successs");
                // the result object has these properties: uid, provider, expiresAtUnixEpochSeconds, profileImageURL, token
                var response = JSON.stringify(result);
                console.log('Login Response : '+response);

                // Route to list page
                this._router.navigate(["List"]);
            },
            function (errorMessage) {
                console.log("Login Error");
                console.log(errorMessage);
            }
        );
    }

    signUp() {
        var result = this._userService.register(this.user,this.config);

        result.then(
            function (result) {
                // dialogs.alert({
                //  title: "User created",
                //  message: "userid: " + result.key,
                //  okButtonText: "Nice!"
                // })
                console.log('User Object Info : '+JSON.stringify(result));
                console.log('User Created with user id - '+result.key);
                alert("Your account was successfully created.");

                this.toggleDisplay();
            },
            function (errorMessage) {
                // dialogs.alert({
                //  title: "No user created",
                //  message: errorMessage,
                //  okButtonText: "OK, got it"
                // })
                console.log('No user created. Got error message insted - '+errorMessage);
                alert("Unfortunately we were unable to create your account.")
            }
        );


    }

    toggleDisplay() {
        this.isLoggingIn = !this.isLoggingIn;
    }
}

Allt fungerar bra förutom vid körning av this._router.navigate(["List"]); från filen login.component.ts får jag följande fel

JS: EXCEPTION: Error: Uncaught (in promise): TypeError: Cannot read property '_router' of null

Kan inte förstå exakt vad felet är.

Hälsningar

Svar #1

Jag misstänker att detta problem har att göra med kontextomfångsproblem. Jag tror att du bör försöka fånga omfånget innan du dyker in i dina then-block. Jag brukar lagra detta i en variabel som kallas that, se nedan (och se kommentarerna):

login() {

    var result = this._userService.login(this.user,this.config);
    var that = this; //store the current context 
    result.then(
        function (result) { //context (this has changed)
            console.log("Login Successs");
            // the result object has these properties: uid, provider, expiresAtUnixEpochSeconds, profileImageURL, token
            var response = JSON.stringify(result);
            console.log('Login Response : '+response);

            // Route to list page
            that._router.navigate(["List"]); //reference _router from the original scope
        },
        function (errorMessage) {
            console.log("Login Error");
            console.log(errorMessage);
        }
    );
}

Svar #2

Ja, du kan dynamiskt binda this som George Edwards påpekade. Ett annat alternativ är att använda pilfunktioner, vilket skulle göra att du kan behålla anroparens scope inuti funktionen så att du inte alltid behöver komma ihåg att skriva var that=this.

login() {

    var result = this._userService.login(this.user,this.config);
    result.then(
        // Within the arrow function 'this' now refers to the lexical context rather than changing depending upon how the function is called.
        (result) => {
            console.log("Login Successs");
            // the result object has these properties: uid, provider, expiresAtUnixEpochSeconds, profileImageURL, token
            var response = JSON.stringify(result);
            console.log('Login Response : '+response);

            // Route to list page
            this._router.navigate(["List"]); //reference _router from the original scope
        },
        // Arguably clearer
        (errorMessage) => {
            console.log("Login Error");
            console.log(errorMessage);
        }
    );
}