Angular 2 - Content not loading in router outlet

社会主义新天地 提交于 2019-12-23 13:16:10

问题


I'm still pretty new to Angular 2, hopefully you guys can help me out.

I have a fairly simple app, there's a login page, after successful login the user is directed to a page with a sidemenu. The login screen doesn't have this sidemenu. When the user logs out he is directed to the login page again.

The problem is that after login the sidemenu becomes visible but the other content is only visible after a refresh. Same thing for logout, after logout the page is blank, only after refresh the content (login page) is displayed. I'm probably doing something wrong but even after looking at other questions I can't figure it out.

Here's my routing:

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';

import { ProfileComponent } from './profile-component/profile-component.component'
import { PageNotFoundComponent } from './page-not-found/page-not-found.component';
import { LoginComponent } from './login/login.component';
import { LoggedInGuard } from './logged-in/logged-in.guard';

const appRoutes: Routes = [
  {path: 'profile', component: ProfileComponent, canActivate: [LoggedInGuard]},
  {path: 'login', component: LoginComponent},
  {path: '', component: LoginComponent},
  {path: '**', component: PageNotFoundComponent},
];

@NgModule({
  imports: [
    RouterModule.forRoot(appRoutes)
  ],
  exports: [
    RouterModule
  ]
})
export class AppRoutingModule {
}

The LoginComponent:

import {Component, OnInit} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {LoginService} from '../login-service/login-service.service';
import {Router} from '@angular/router';


@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss'],
  providers: [ LoginService ]
})
export class LoginComponent implements OnInit {

  loginForm: FormGroup;
  error: String;

  constructor(private formBuilder: FormBuilder,
              private loginService: LoginService,
              private router: Router) {
  }


  ngOnInit() {
    if (this.loginService.isLoggedIn()) {
      this.router.navigate(['/profile']);
    }
    this.loginForm = this.formBuilder.group({
      username: ['', Validators.required],
      password: ['', Validators.required]
    });
  }

  login(): void {
    let self = this;
    this.loginService.login(this.loginForm.value.username, this.loginForm.value.password).subscribe(function (result) {
      if (result) {
        self.router.navigate(['/profile']);
      }

    }, function (error) {
      self.error = 'Invalid';
    });
  }
}

The AppComponent HTML looks like this:

<md-toolbar color="primary" *ngIf="isLoggedIn()">
  <span>Sporter volgsysteem</span>
</md-toolbar>

<md-sidenav-layout *ngIf="isLoggedIn()">
  <md-sidenav #start mode="side" [opened]="true">
      <a routerLink="/add" routerLinkActive="active" md-button color="primary" disabled="false"><md-icon class="icon">add</md-icon><span class="nav-item">Toevoegen</span></a>
      <a routerLink="/compare" routerLinkActive="active" md-button color="primary"><md-icon class="icon">swap_horiz</md-icon><span class="nav-item">Vergelijken</span></a>
      <a routerLink="/search" routerLinkActive="active" md-button color="primary"><md-icon class="icon">search</md-icon><span class="nav-item">Zoeken</span></a>
      <a routerLink="/profile" routerLinkActive="active" md-button color="primary"><md-icon class="icon">account_box</md-icon><span class="nav-item">Profiel</span></a>
      <a routerLink="/feedback" routerLinkActive="active" md-button color="primary"><md-icon class="icon">feedback</md-icon><span class="nav-item">Feedback</span></a>
      <a routerLink="/faq" routerLinkActive="active" md-button color="primary"><md-icon class="icon">info</md-icon><span class="nav-item">FAQ</span></a>

      <div class="spacer"></div>

      <a md-button color="primary" routerLink="/login" (click)="logout()"><md-icon class="icon">exit_to_app</md-icon><span class="nav-item">Uitloggen</span></a>
  </md-sidenav>


  <router-outlet></router-outlet>
</md-sidenav-layout>


<router-outlet *ngIf="!isLoggedIn()"></router-outlet>

AppComponent:

import {Component} from '@angular/core';
import {LoginService} from "./login-service/login-service.service";
import {Router } from '@angular/router'


@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.scss'],
  providers: []
})
export class AppComponent {

  constructor(private loginService : LoginService,
              private router: Router) {
  }

  logout() {
    this.loginService.logout();
    this.router.navigate(['/login']);
  }

  isLoggedIn() {
    return this.loginService.isLoggedIn();
  }

}

So why isn't the content of the ProfileComponent displaying after login, and why isn't the login page displaying after logout, but both display when you refresh?

Update

Most suggested that it is due to multiple unnamed router-outlets so to verify that I removed one of the outlets and show the sidemenu layout all the time. For testing purposes of course. That doesn't solve the problem, it gives me the same behaviour: the profile content is only loaded after refresh.

Update 2 I'm guessing this is related to using *ngIf to display the router-outlet


回答1:


I believe the issue is the double router-outlet as the comments above suggest. I did something like this in my project.

This was my solution:

I first created a service to determine the state of the login (I realize that event emitter is not good practice, I'm working on changing this to a behavior subject):

GlobalEventsManager

import { EventEmitter, Injectable } from '@angular/core';


@Injectable()
export class GlobalEventsManager {
     public showNavBar: EventEmitter<boolean> = new EventEmitter<boolean>();



}

I used an authguard for my routes that checks if the login token is not expired. I used the angular2-jwt library for this

AuthGuard

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { tokenNotExpired } from 'angular2-jwt';
import { GlobalEventsManager } from './GlobalEventsManager';


    @Injectable()
    export class AuthGuard implements CanActivate {

        constructor(private router: Router, private globaleventsManager: GlobalEventsManager) {}
        canActivate(next: ActivatedRouteSnapshot,
                    state: RouterStateSnapshot) {

        if (tokenNotExpired('currentUser')) {
            this.globaleventsManager.showNavBar.emit(true);
            return true;

        } else {
            localStorage.removeItem('currentUser');
            this.router.navigate(['/login']);
            return false;
        }

        }


    }

When a user logs in using my login component, I set the GlobalEventsManager to true

Login

constructor(
        private router: Router,
        private authenticationService: AuthenticationService,
        private globaleventsManager: GlobalEventsManager) { }

ngOnInit() {
        // reset login status

        this.authenticationService.logout();
        this.globaleventsManager.showNavBar.emit(false);
    }

    login() {
        this.loading = true;
        this.authenticationService.login(this.model.username, this.model.password)
            .subscribe( (result) => {
                this.globaleventsManager.showNavBar.emit(true);
                this.router.navigate(['/']);

In my navbar I subscribe to the GlobalEventsManager and set a boolean property to the result:

Navbar

import { Component } from '@angular/core';
import { Router } from '@angular/router';
import { NavActiveService } from '../../../services/navactive.service';
import { GlobalEventsManager } from '../../../services/GlobalEventsManager';

@Component({
  moduleId: module.id,
  selector: 'my-navbar',
  templateUrl: 'navbar.component.html',
  styleUrls:['navbar.component.css'],  
})
export class NavComponent {
  showNavBar: boolean = true;

  constructor(private router: Router,              
              private globalEventsManager: GlobalEventsManager){

    this.globalEventsManager.showNavBar.subscribe((mode:boolean)=>{
      this.showNavBar = mode;
    });

  }

}

In my navbar HTML I can now create two navbars, one navbar simply has a login nav and when my authguard notices that a toekn is expired or a user is not logged in it displays the navbar with just login as an option. Once logged in, the GlobalEventsManager value changes and my logged in navbar is displayed:

Navbar HTML

<div *ngIf="showNavBar">
//My logged in navbar 
</div>
<div *ngIf="!showNavBar">
//navbar that just displays login as an option
</div>



回答2:


Following the comments above from @bhetzie and @jaime-torres and my own hypothesis I investigated further. It turns out that the problem is indeed with the double router-outlet.

My solution was to extract a LoginModule module and then in that module redirect to the login page. In all other cases I route to yet another module called SvsModule which displays the logged in content with a side nav structure.

So the SvsModule has the following routing:

RouterModule.forChild([
  {path: 'svs', component: ProfileComponent, canActivate:[LoggedInGuard]},
  {path: 'svs/profile', component: ProfileComponent, canActivate:[LoggedInGuard]}
])

And the ProfileComponent uses the following HTML:

<md-sidenav-layout>
  <md-sidenav #start mode="side" [opened]="true">
    <md-nav-list>
      <a routerLink="/add" routerLinkActive="active" md-button color="primary" disabled="false">
        <md-icon class="icon">add</md-icon>
        <span class="nav-item">Toevoegen</span>
      </a>
      <a routerLink="/compare" routerLinkActive="active" md-button color="primary">
        <md-icon class="icon">swap_horiz</md-icon>
        <span class="nav-item">Vergelijken</span>
      </a>
      <a routerLink="/search" routerLinkActive="active" md-button color="primary">
        <md-icon class="icon">search</md-icon>
        <span class="nav-item">Zoeken</span>
      </a>
      <a routerLink="/profile" routerLinkActive="active" md-button color="primary">
        <md-icon class="icon">account_box</md-icon>
        <span class="nav-item">Profiel</span>
      </a>
      <a routerLink="/feedback" routerLinkActive="active" md-button color="primary">
        <md-icon class="icon">feedback</md-icon>
        <span class="nav-item">Feedback</span>
      </a>
      <a routerLink="/faq" routerLinkActive="active" md-button color="primary">
        <md-icon class="icon">info</md-icon>
        <span class="nav-item">FAQ</span>
      </a>

      <div class="spacer"></div>

      <a md-button color="primary" routerLink="/login" (click)="logout()">
        <md-icon class="icon">exit_to_app</md-icon>
        <span class="nav-item">Uitloggen</span></a>
    </md-nav-list>

  </md-sidenav>
  <router-outlet></router-outlet>

</md-sidenav-layout>

I will accept this as the answer, but thanks to aforementioned users to help me on my path.




回答3:


Put your navigate call in a ngZone. (Angular 5)

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

constructor(
    private ngZone: NgZone,
    private router: Router
) {}

...

this.ngZone.run( () => {
    this.router.navigate(['/profile']);
});


来源:https://stackoverflow.com/questions/40767142/angular-2-content-not-loading-in-router-outlet

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!