简体   繁体   English

Angular 4,路由器4.0.0:routerLink或router.navigate不会以编程方式重定向到子路由

[英]Angular 4, Router 4.0.0: routerLink or router.navigate programatically doesn't redirect to child routes

I'm trying to create a link to get redirected after login(/signin route) to a /dashboard/overview child route without any luck. 我正在尝试创建一个链接,以在登录(/登录路由)后重定向到/ dashboard / overview子路由而没有任何运气。 I click the link and I get no errors nor any response. 我单击链接,没有错误也没有任何回应。 I can see the path on the bottom bar of the browser that is correct and If I enter the url manually I access the correct page, the route is /dashboard/overview . 我可以在浏览器的底部栏中看到正确的路径,如果手动输入URL,则访问正确的页面,路由为/ dashboard / overview One thing I'm not sure if it has anything to do is that the route is AuthGuarded. 我不确定是否有任何事情要做的一件事是路由是否为AuthGuarded。

I tried both programatically after sign in redirect the user to a dashboard, I can even see the 'redirecting to dashboard' message on chrome console 登录后,我以编程方式尝试了两种操作,将用户重定向到仪表板,甚至可以在chrome控制台上看到“重定向到仪表板”消息

onSignin(form: NgForm){
    const email = form.value.email;
    const password = form.value.password;
    this.user = {email, password};
     this.authServiceSubscription = this.authService.signinUser(this.user).subscribe(
       (response) => {
         //redirect to dashboard
        const loginResultCode = response.login_result_code;
         if (loginResultCode == "SUCCESS") {
                       console.log("Sponsor logged in");
                       this.authService.changeStatusToAuthenticated();
                       //redirect to dashboard
                       console.log('Redirecting to dashboard');
                       this.router.navigate(['/dashboard/overview']);
                   } else {
                       console.log("There were errors with the data");
                       //present errors to the user
                       this.errorMessage = "Los datos de autenticación son incorrectos. Intente nuevamente";
                   }
       },
       (error) => { console.log("Error Login", error); this.errorMessage = "Hubo un error interno, intente de nuevo mas tarde";}
       );
  }

And also creating a routerLink, but it doesn't work either, nothing happens, not even an error in the console: 并且还创建了routerLink,但是它也不起作用,什么也没有发生,甚至在控制台中也没有错误:

  <li><a style="cursor: pointer;" routerLink="/dashboard/overview">Go To Dashboard</a></li>

Here's my routing file: 这是我的路由文件:

const appRoutes: Routes = [
  { path: '', redirectTo: '/', pathMatch:'full'},
  { path: '', component: MainComponent },
  { path: 'signin', component:SigninComponent},
  { path: 'signup', component: SignupComponent},
  { path: 'dashboard', canActivate:[AuthGuard],component: DashboardComponent,
    children: [
      { path: '', redirectTo:'/dashboard/overview', pathMatch: 'full'},
      { path: 'overview', component: OverviewCampaignsComponent },
      { path: 'active', component: ActiveCampaignsComponent},
      { path: 'history', component: HistoryCampaignsComponent}
    ] },
  { path: 'not-found', component: ErrorPageComponent },
  { path: '**', redirectTo: '/not-found' }

]

I even put a console.log on the ngOnInit of the Dashboard component to see if the component gets created, or in the overview component but I didn't have any luck, I couldn't see any messages on console when navigating programatically nor with routerLink. 我什至将console.log放在仪表板组件的ngOnInit上,以查看是否已创建该组件,或者在概述组件中,但是我没有任何运气,当以编程方式导航或使用导航时,我在控制台上看不到任何消息routerLink。 I did get the message when I accessed manually as I stated above. 如上所述,当我手动访问时,确实收到了消息。 Any ideas? 有任何想法吗? Thank you very much 非常感谢你

EDIT: Apparently is a problem with the authguard I'm applying to the dashboard route, this is the AuthGuard file, could it be that it's not catching some error or maybe the returned values are not the ones that should be??: 编辑:显然是我应用于仪表板路线的authguard的问题,这是AuthGuard文件,可能是它没有捕获某些错误,或者返回的值不是应该的值?:

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';


@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.authService.isAuthenticated().map(isAuth => {
      if (isAuth){
        console.log("Auth Guard approves the access");
        return true;
      }
      else {
        console.log('AuthGuard Denying route, redirecting to signin');
        this.router.navigate(['/signin']);
        return false;
      }
    });
  }
}

the isAuthenticated() method on the authService just returns an observable with the auth state of the user. authService上的isAuthenticated()方法仅返回具有用户auth状态的observable。 I wonder if there's a race condition or something...cause that observable gets set initially by making an http async request....If I put a console.log in isAuthenticated method it gets logged on the console. 我想知道是否存在争用条件或什么...导致可观察性最初是通过发出http异步请求来设置的。...如果我将console.log放在isAuthenticated方法中,它将被记录在控制台上。 If I put a console.log inside the map on the authguard function in or out the if it doesn't get logged so for some reason that code is not being executed.... 如果我将console.log放进或放出authguard函数的映射内,则如果未记录,则由于某种原因未执行代码。

auth.service.ts 验证服务

    import { Injectable, OnInit } from '@angular/core';
    import { Router } from '@angular/router';
    import { Http, Response, RequestOptions, Headers } from '@angular/http';
    import 'rxjs/add/operator/map';
    import {Observable, Subject} from "rxjs/Rx";

    @Injectable()
    export class AuthService implements OnInit {
     userIsAuthenticated = new Subject();
      constructor(private router: Router, private http: Http) {
        this.ngOnInit();
      }

      private getHeaders(){
          let headers = new Headers();
          headers.append('Content-Type', 'application/json');
          headers.append('Accept', 'application/json');
          headers.append('Authorization','Bearer');
          return headers;
      }

      ngOnInit(){

        this.changeStatusToUnauthenticated();
        //initial check with the server
        let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });

       this.http.get('http://localhost:3000/api/sponsor/check/login',options)
          .map(response => {
        console.log("Execute this");
            if (response.status === 200) {
              console.log("execute also this");
              this.changeStatusToAuthenticated();
              return Observable.of(true);
            }
          }
        ).catch((err)=>{
          //maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
          if(err.status === 403){
            console.log('Forbidden 403');
    //        If I want to redirect the user uncomment this line
    //        this.router.navigate(['/signin']);
          }
          this.changeStatusToUnauthenticated();
          return Observable.of(false);
        }).subscribe((isAuth)=>{
           console.log("Initial refresh auth state ", isAuth);
        });

      }

      isAuthenticated(): Observable<boolean> {
  if(this.userIsAuthenticated){
 //if I change this line for return Observable.of(true) it works
   return this.userIsAuthenticated;
 }else{
   return Observable.of(false);
 }
  }

      logout() {
        console.log('logging out');
        let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
        return this.http.get('http://localhost:3000/api/sponsor/logout/', options).map(res=>res.json())
        .subscribe(
            (response) => {
              //redirect to dashboard
             const logoutResultCode = response.code;
              if (logoutResultCode == "200") {
                            console.log("Sponsor logged out successfully");
                            //redirect to dashboard
                            this.changeStatusToUnauthenticated();
                            this.router.navigate(['/signin']);
                        }
            },
            (error) => {
              console.log("Error Logout- Header", error);
              //check for 403 if it's forbidden or a connection error
              this.changeStatusToUnauthenticated();
              this.router.navigate(['/signin']);}
          );

      }

      signinUser(user) {
        console.log("Logging user");
        let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
        return this.http.post('http://localhost:3000/api/sponsor/login/', user, options).map(
          response => response.json());
      }

      registerUser(user) {
        let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
        return this.http.post('http://localhost:3000/api/sponsor/register/', user, options).map(
          response => response.json());
      }

      changeStatusToUnauthenticated(){
        this.userIsAuthenticated.next(false);
      }

      changeStatusToAuthenticated(){
        this.userIsAuthenticated.next(true);
      }


    }

EDIT 2: I used Behaviour Subject instead of Subject on the authService cause it lets me get the last emitted value which is a pretty cool feature compared to the regular subject in which you have to subscribe, which sometimes is not enough. 编辑2:我使用行为主题,而不是authService上的主题,因为它使我获得了最后发出的值,这与必须订阅的常规主题相比是一个很酷的功能,有时这还不够。 More details on my answer below. 以下是我的答案的更多详细信息。

In the end the problem was in what the authService returned on the isAuthenticated() method, I was not returning a resolved value apparently according to the logs, so the authguard got stucked before being able to resolve the route to the component. 最后,问题出在authService在isAuthenticated()方法上返回的内容,我显然没有根据日志返回已解析的值,因此authguard在能够解析到组件的路由之前被卡住了。 I solved my question by searching on the rxjs documentation. 我通过搜索rxjs文档解决了我的问题。 I found the BehaviorSubject https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md 我发现了BehaviorSubject https://github.com/Reactive-Extensions/RxJS/blob/master/doc/api/subjects/behaviorsubject.md

It lets you get the last value emmited so I can return an Observable.of(userIsAuthenticated.getValue()) and deliver it to the AuthGuard and it works perfectly now. 它可以让您获取最后一次发出的值,这样我就可以返回Observable.of(userIsAuthenticated.getValue())并将其传递给AuthGuard,并且现在可以正常使用了。 I added the logic that if the last emitted value was false then I do a dummy request to decide if the user should be sent to the login screen. 我添加了逻辑,如果最后发出的值是false,那么我将执行一个虚拟请求,以决定是否应将用户发送到登录屏幕。 Then this goes hand in hand with changing the value of the BehaviourSubject to false if I get an http forbidden response on EVERY request I make to the server. 然后这与将BehaviourSubject的值更改为false紧密结合,如果我对服务器发出的每个请求均收到HTTP禁止响应。 This things combined will assure consistency between frontend and backend traditional sessions, avoiding the expired session on the backend and non-expired state on the frontend. 这些内容的结合将确保前端和后端传统会话之间的一致性,避免后端上的过期会话和前端上的未过期状态。 Hope this helps someone. 希望这对某人有帮助。 The code: 编码:

auth.service.ts 验证服务

        @Injectable()
        export class AuthService implements OnInit {
         userIsAuthenticated= new BehaviorSubject(null);
          constructor(private router: Router, private http: Http) {
            this.ngOnInit();
          }

          private getHeaders(){
              let headers = new Headers();
              headers.append('Content-Type', 'application/json');
              headers.append('Accept', 'application/json');
              headers.append('Authorization','Bearer');
              return headers;
          }


      ngOnInit() {
        //initial check with the server
        this.doAuthCheck();

      }

      doAuthCheck(): Observable<boolean> {

        let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });

        return this.http.get('http://localhost:3000/api/check/login', options)
          .map(response => {
            if (response.status === 200) {
              this.changeStatusToAuthenticated();
              return Observable.of(true);
            }
          }
          ).catch((err) => {
            //maybe add in the future if the code is 403 then send him to login otherwise send him elsewhere
            if (err.status === 403) {
              console.log('Forbidden 403');
              //        If I want to redirect the user uncomment this line
              //        this.router.navigate(['/signin']);
            }
            this.changeStatusToUnauthenticated();
            return Observable.of(false);
          });
      }

      isAuthenticated(): Observable<boolean> {
        const isAuth = this.userIsAuthenticated.getValue();
        if (isAuth) {
          return Observable.of(isAuth);
        } else {
          return this.doAuthCheck();
        }
      }


  logout() {
    console.log('logging out');
    let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
    return this.http.get('http://localhost:3000/api/logout/', options).map(res => res.json())
      .subscribe(
      (response) => {
        //redirect to dashboard
        const logoutResultCode = response.code;
        if (logoutResultCode == "200") {
          console.log("logged out successfully");
          //redirect to dashboard
          this.changeStatusToUnauthenticated();
          this.router.navigate(['/signin']);
        }
      },
      (error) => {
        console.log("Error Logout- Header", error);
        //check for 403 if it's forbidden or a connection error
        this.changeStatusToUnauthenticated();
        this.router.navigate(['/signin']);
      }
      );

  }



  signinUser(user) {
    console.log("Logging user");
    let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
    return this.http.post('http://localhost:3000/api/login/', user, options).map(
      response => response.json());
  }

  registerUser(user) {
    let options = new RequestOptions({ headers: this.getHeaders(), withCredentials: true });
    return this.http.post('http://localhost:3000/api/register/', user, options).map(
      response => response.json());
  }

changeStatusToUnauthenticated() {
    this.userIsAuthenticated.next(false);
  }

  changeStatusToAuthenticated() {
    this.userIsAuthenticated.next(true);
  }

        }

auth-guard.service.ts auth-guard.service.ts

import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from './auth.service';
import { Observable } from 'rxjs/Observable';


@Injectable()
export class AuthGuard implements CanActivate {

  constructor(private authService: AuthService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
    return this.authService.isAuthenticated().map(isAuth => {
      console.log("is Authenticated",isAuth);
      if (isAuth){
        console.log("Auth Guard approves the access");
        return true;
      }
      else {
        console.log('AuthGuard Denying route, redirecting to signin');
        this.router.navigate(['/signin']);
        return false;
      }
    });
  }
}

routes file 路线文件

const appRoutes: Routes = [
  { path: '', redirectTo: '/', pathMatch:'full'},
  { path: '', component: MainComponent },
  { path: 'signin', component:SigninComponent},
  { path: 'signup', component: SignupComponent},
  { path: 'dashboard', canActivate:[AuthGuard],component: DashboardComponent,
    children: [
      { path: '', redirectTo:'/dashboard/overview', pathMatch: 'full'},
      { path: 'overview', component: OverviewCampaignsComponent },
      { path: 'active', component: ActiveCampaignsComponent},
      { path: 'history', component: HistoryCampaignsComponent}
    ] },
  { path: 'not-found', component: ErrorPageComponent },
  { path: '**', redirectTo: '/not-found' }

]

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM