Angular2:使用路由,登录成功后如何显示导航栏?

Angular2: Using routes, how to display the navigation bar after successfully logged in?

我正在尝试显示导航栏,一旦用户成功完成。

例如:

如何在"AppComponent"里面改变"showMenu"属性在"LoginComponent"?重要提示:我正在使用路由。

app.ts:

@Component({
  selector: 'app',
  template: `<div *ngIf="showMenu">
               <fnd-menu-nav></fnd-menu-nav>
             </div>
             <router-outlet></router-outlet>
              `,
  directives: [ROUTER_DIRECTIVES, MenuNavComponent]
})
@RouteConfig([
  { path: '/login', name: 'Login', component: LoginComponent, useAsDefault: true },
  { path: '/welcome', name: 'Welcome', component: WelcomeComponent }
])
export class AppComponent {
  public showMenu : boolean;
}

login.component.ts:

@Component({
  selector: 'fnd-login',
  templateUrl: './fnd/login/components/login.component.html',
  providers: [LoginService]
})
export class LoginComponent {
  /* .. other properties */

  constructor(private _router: Router, private _loginService: LoginService ) {
  }
  /* .. other methods  */
  /* .. other methods  */


  private onLoginSuccessfully(data : any) : void {
    /* --> HERE: Set showMenu in AppComponent to true. How? */
    this._router.navigate(['Welcome']);

  }
}

或者这个设计不是最好的解决方法?

我最近做了类似的事情,这就是我的做法。首先,您需要在应用的根目录创建一个 NavBarComponent。在 NavBarComponent 中,您引用(我称之为)一个 GlobalEventsManager,它是您在需要的地方注入的服务。

下面是 GlobalEventsManager:

import { Injectable } from '@angular/core';
import { BehaviorSubject } from "rxjs/BehaviorSubject";
import { Observable } from "rxjs/Observable";

@Injectable()
export class GlobalEventsManager {

    private _showNavBar: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);
    public showNavBarEmitter: Observable<boolean> = this._showNavBar.asObservable();

    constructor() {}

    showNavBar(ifShow: boolean) {
        this._showNavBar.next(ifShow);
    }


}

现在您将 GlobalEventsManger 服务注入您的登录组件(类似这样)

import {GlobalEventsManager} from "./../GlobalEventsManager";

@Component({
  selector: 'fnd-login',
  templateUrl: './fnd/login/components/login.component.html',
  providers: [LoginService]
})
export class LoginComponent {
  /* .. other properties */

  constructor(private _router: Router, private _loginService: LoginService, private globalEventsManager: GlobalEventsManager) {
  }
  /* .. other methods  */
  /* .. other methods  */


  private onLoginSuccessfully(data : any) : void {
    /* --> HERE: you tell the global event manger to show the nav bar */
    this.globalEventsManger.showNavBar(true);
    this._router.navigate(['Welcome']);

  }
}
在您的 NavBarComponent 中,您订阅了 showNavBar 事件发射器:

import {Component, OnInit} from "@angular/core";
import {GlobalEventsManager} from "../GlobalEventsManager";
@Component({
    selector: "navbar",
    templateUrl: "app/main/topNavbar/TopNavbar.html"
})

export class TopNavbarComponent  {
    showNavBar: boolean = false;


    constructor(private globalEventsManager: GlobalEventsManager) { 
        this.globalEventsManager.showNavBarEmitter.subscribe((mode)=>{
            
            this.showNavBar = mode;
        });
        
    }

 
}
使用模板中的 *ngIf="showNavBar" HTML 到 hide/show 导航栏。

您的应用程序组件看起来像这样:

@Component({
  selector: 'app',
  template: `<navbar></navbar>
             <router-outlet></router-outlet>
              `
})
export class AppComponent {
  //This doesn't belong here --> public showMenu : boolean;
}

启动应用程序时还必须注册 GlobalEventsManager:

import { GlobalEventsManager } from './GlobalEventsManager';
import { TopNavbarComponent } from './TopNavbarComponent';

@NgModule({
    bootstrap: [App],
    declarations: [
        App,
        TopNavbarComponent
    ],
    imports: [
        BrowserModule
    ],
    providers: [GlobalEventsManager]
})
export class AppModule {
}

应该可以了。

更新:我已经更新了这个答案以反映在组件外部(即在服务中)使用事件的更可接受的方式;这需要使用 BehaviorSubject/Observable 而不是 EventEmitter

实际上有一种完全不同的方法,它不使用任何事件发射器/侦听器。我不反对这些事件,我根据特定的项目需求/复杂性使用这两种方法(下面的一种和@brando 的一种)。

方法:我们有 2 个应用程序模块(区域):public(没有导航栏的)和受保护的(有导航栏的)。

Public 模块包含所有 public 路由:

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { RegistrationComponent } from './registration/registration.component';
import { ResetPasswordComponent } from './reset-password/reset-password.component';

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([
      { path: 'login', component: LoginComponent },
      { path: 'registration', component: RegistrationComponent },
      { path: 'reset-password', component: ResetPasswordComponent }
    ])
  ],
  declarations: [
    LoginComponent,
    RegistrationComponent,
    ResetPasswordComponent
  ]
})
export class PublicModule { }

这是你应该有的,这里没有什么不寻常的。

然后我们有一个保护区

import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { RouterModule } from '@angular/router';
import { NavbarWrapperComponent } from './navbar-wrapper/navbar-wrapper.component';
import { UserProfileComponent } from './user-profile/user-profile.component';

@NgModule({
  imports: [
    CommonModule,
    RouterModule.forChild([
      { path: '', redirectTo: '/login', pathMatch: 'full' }, // point 1
      {
        path: '', // point 2
        component: NavbarWrapperComponent, // point 3
        children: [
          { path: 'profile', component: UserProfileComponent }
        ]
      }
    ])
  ],
  declarations: [
    NavbarWrapperComponent,
    UserProfileComponent
  ]
})
export class ProtectedModule { }

魔法从这里开始。

首先注意点1:

{ path: '', redirectTo: '/login', pathMatch: 'full' },

我们需要这个就在这里。如果我们将它放入 AppModule,它将被忽略。这里没有什么重要的,在受保护的模块中进行重定向可能更合乎逻辑。

点 2 允许我们将所有 children 路由代理到 NavbarWrapperComponent点 3)它负责渲染我们所有的 children。这是导航栏组件的模板:

<nav class="navbar navbar-toggleable-md navbar-light bg-faded">
  <!-- nav content here -->
</nav>

<router-outlet></router-outlet>

<router-outlet> 将处理所有 children 路线。

您可能遇到的问题及其解决方案:

  • 您可能希望将重定向到 AppModule - 只需将 点 2 中的路径更改为某个真实姓名,例如protected。这将在您所有受保护的网址前加上您可能不想要的这个值。您有2个选项可以选择。
  • 您可能希望在保护区内拥有不止一个模块 - 只需使用 lazy routing
  • 您可能想要隐藏/显示导航、传递参数等 - 只需将其与事件解决方案结合使用即可。

这种方式可能看起来不太灵活,但它确实有效并且几乎涵盖了您可能需要的所有情况。它没有事件的复杂性,充分利用了 Router 的特性。这里的杀手锏 - 它非常简单,非常容易理解和维护。

angular4 中最好的现代方式,带有新路由器,带有子路由,只需要使用 UrlSerializer-class 删除括号,https://angular.io/docs/ts/latest/api/router/index/UrlSerializer-class.html, 有人用过吗?

export const ROUTES: Routes = [
  { path: 'login', component: LoginComponent },
  {
    path : '',
    children: [
        {
          path: '', component: DefaultLayoutComponent,
          children: [
            { path: '', component: HomeComponent, canActivate: [AuthGuard] },
            { path: 'users', component: UsersListComponent, canActivate: [AuthGuard] },
            { path: 'users-add', component: UsersAddComponent, canActivate: [AuthGuard] },
            { path: 'users-view/:id', component: UsersViewComponent, canActivate: [AuthGuard] },
            { path: 'users-edit/:id', component: UsersEditComponent, canActivate: [AuthGuard] },
            ]
        }
    ]
  },
  // otherwise redirect to home
  { path: '**', redirectTo: '' }
]