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: '' }
]
我正在尝试显示导航栏,一旦用户成功完成。
例如:
如何在"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']);
}
}
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;
});
}
}
您的应用程序组件看起来像这样:
@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: '' }
]