-->

angular2 router.navigate inside auth0 callback

2020-07-13 08:33发布

问题:

I am having an issue rendering a template after calling router.navigate inside a callback for auth0lock

loginComponent.ts

import {Component, Inject} from 'angular2/core';
import {Router, ComponentInstruction} from 'angular2/router';

import {Auth} from '../auth';

declare var Auth0Lock;

@Component({
    selector: 'login',
    templateUrl: '/tpls/login/login.html'
})

export class LoginComponent {

    private lock = new Auth0Lock('xxx', 'xxx.auth0.com');

    constructor(@Inject(Router) private router: Router, @Inject(Auth) private auth: Auth) { }

    logError = (err) => {
        console.log(err);
    }

    loginSuccess = (data) => {
        if(this.router.parent.lastNavigationAttempt !== undefined && this.router.parent.lastNavigationAttempt !== '/Login') {
            this.router.navigateByUrl(this.router.parent.lastNavigationAttempt);
        } else if(data.user.req_update) {
            this.router.navigate(['Profile']);
        } else {
            this.router.navigate(['Home']);
        }
    }

    ngOnInit() {

        this.lock.show((err: Error, profile: any, id_token: string) => {
            if(err) return this.logError(err);
            return this.auth.login(profile, id_token);
        }); 

        this.auth.loginSuccess.subscribe(
            data => this.loginSuccess(data),
            err => this.logError(err)
        );

    }
}

auth.ts

import {Injectable, Inject, EventEmitter, Output } from 'angular2/core';
import {Http, Headers} from 'angular2/http';

@Injectable()

export class Auth {
    ...
    @Output() loginSuccess = new EventEmitter();

    login = (profile, id_token) => {
        ...

        this.addUser(profile).subscribe(
            data => {
                this.loginSuccess.next(data.json());
            },
            err => {
                console.log(err); 
                this.loginSuccess.error(err.json());
            }
        );
    }
    addUser = (user: any) => {
        let body = JSON.stringify(user);
        return this.http.post('/api/user/add', body, { headers: this.headers});
    }
}

homeComponent.ts

import {Component, Inject, OnInit} from 'angular2/core';
import {Http} from 'angular2/http';
import {ROUTER_DIRECTIVES} from 'angular2/router'

import {Auth} from '../auth';
import {Post} from '../post/Post';
import {IPost} from '../post/IPost';
import {AuthorComponent} from '../author/authorComponent';

@Component({
    selector: 'home',
    templateUrl: '/tpls/home/home.html',
    directives: [ROUTER_DIRECTIVES, AuthorComponent]
})

export class HomeComponent implements OnInit {

    private postService: Post;
    private posts: IPost[];

    constructor(@Inject(Http) private http: Http, @Inject(Auth) private auth: Auth) {
        console.log('constructor');
        this.postService = new Post(this.http, this.auth);
        this.getPosts();
    }

    getPosts = () => {
        this.postService.all().subscribe(
            data => this.getPostsCallback(data.json()),
            err => this.logError(err)
        );
    }

    getPostsCallback = (data) => {
        console.log('callback');
        this.posts = data;
    }

    logError = (err) => {
        console.log(err);
    }

    ngOnInit() {
        console.log('init');
        //this.postService = new Post(this.http, this.auth);
        //this.getPosts();
    }

}

I am including the cdn script for authlock in my index page. Seems like any route I navigate to after login does not render. The callback from this.lock.show works and I can read the variables. Any advice is greatly appreciated.

basic concept: https://plnkr.co/edit/Oz8lY7v6q8GpH71WLtAK

回答1:

This Should fix your problem.

import { Component, NgZone } from '@angular/core';

constructor(public router: Router, public _zone: NgZone) {}

then inside your callback , call this

this._zone.run(()=>{
  this.router.navigate(['uploader']);
});


回答2:

Thanks to this auth0 article I found a solution to the problem, since NgZone wasn't working for me.

Wrapping the request in bindNodeCallback did the trick. This function wraps the Auth0 request with Zone.js and thus doing the request in the Angular zone.

My code was:

this._auth0.parseHash((err, authResult: auth0.Auth0DecodedHash) => {
    if (!err && authResult && authResult.accessToken && authResult.idToken) {
       window.location.hash = ''; // Remove the Auth0 trailing hash
    }

    this.ngZone.run(() => {
       this.router.navigate(['<some-url>']);
    });
});

But that resulted in not navigating to any page due to being out of Angular scope. I replaced that code with a Zone.js wrapped version to make it work:

this.parseHash$ = bindNodeCallback(this._auth0.parseHash.bind(this._auth0));
this.parseHash$().subscribe(() => this.router.navigate(['<some-url>']));

And editing the request result can be done in the subscribe or via the map functions.


Versions

For the code above I am using the following versions:

Angular 6.1.7

auth0-js 9.8.0