官网:https://angular.io/api/router…
Acts as a placeholder that Angular dynamically fills based on the current router state.
RouterOutlet 作为 place holder,Angular 会基于以后路由状态动静地填充内容进来。
应用的 selector 正是 router-outlet. 如下图第 60 行代码所示。
Each outlet can have a unique name, determined by the optional name attribute. The name cannot be set or changed dynamically. If not set, default value is “primary”.
每个 router-outlet 能够调配一个 name,默认的名称是 ==primary==.
<router-outlet></router-outlet>
<router-outlet name='left'></router-outlet>
<router-outlet name='right'></router-outlet>
A router outlet emits an activate event when a new component is instantiated, and a deactivate event when a component is destroyed.
当一个新的 Component 被实例化时,routerOutlet 会发送一个 activate 事件。应用程序能够监听这个事件。
<router-outlet
(activate)='onActivate($event)'
(deactivate)='onDeactivate($event)'></router-outlet>
路由配置对象: Route Object
Each definition translates to a Route object which has two things: a path, the URL path segment for this route; and a component, the component associated with this route.
The router draws upon its registry of definitions when the browser URL changes or when application code tells the router to navigate along a route path.
当浏览器的 url 发生变化,或者应用程序调用 router 的路由办法时,Angular router 就依据这些注册的定义,进行新 Component 的绘制。
When the browser’s location URL changes to match the path segment /XXX, then the router activates an instance of the YComponent and displays its view.
例如,当浏览器地址栏的 url 变成 /XXX 时,router 激活 XXX 对应的 Component Y 的一个实例,而后显示其视图。
In order to use the Router, you must first register the RouterModule from the @angular/router package.
要应用 Angular 路由性能,必须先从 @angular/router 包里导入 RouterModule:
Define an array of routes, appRoutes, and pass them to the RouterModule.forRoot() method.
定义一个蕴含路由信息的数组,传入 RouterModule.forRoot 办法里。
The RouterModule.forRoot() method returns a module that contains the configured Router service provider, plus other providers that the routing library requires.
RouterModule.forRoot 办法返回一个新的 module,蕴含了配置好的 Router service provider,加上其余路由库实现须要的 provider.
You might have noticed that the CLI adds RouterModule.forRoot(routes) to the AppRoutingModule imports array. This lets Angular know that the AppRoutingModule is a routing module and forRoot() specifies that this is the root routing module. It configures all the routes you pass to it, gives you access to the router directives, and registers the Router service. Use forRoot() only once in the application, inside the AppRoutingModule.
Once the application is bootstrapped, the Router performs the initial navigation based on the current browser URL.
当 Angular 利用实现初始化之后,Router 基于以后浏览器的默认 url,进行初始跳转动作。
RouterModule provides the Router service, as well as router directives, such as ==RouterOutlet== and
routerLink
.
RouterModule 提供 Router 服务,Router directive,比方 RouterOutlet 和 RouterLink.
The root application module imports RouterModule so that the application has a Router and the root application components can access the router directives.
Root 利用 module 导入 RouterModule,这样利用能够应用 Router 服务,并且利用 Component 能够拜访 router 指令。
Any feature modules must also import RouterModule so that their components can place router directives into their templates.
任何 feature module 都必须导入 RouterModule,只有这样,feature module 蕴含的 Component 能力在其 template 里应用 router 指令。
If the RouterModule didn’t have forRoot() then each feature module would instantiate a new Router instance, which would break the application as there can only be one Router. By using the forRoot() method, the root application module imports RouterModule.forRoot(…) and gets a Router, and all feature modules import RouterModule.forChild(…) which does not instantiate another Router.
Router 实例的单例准则。
How forRoot() works
forRoot() takes a service configuration object and returns a ModuleWithProviders, which is a simple object with the following properties:
- ngModule: in this example, the GreetingModule class
- providers: the configured providers
例子:
const appRoutes: Routes = [{ path: 'crisis-center', component: CrisisListComponent},
{path: 'heroes', component: HeroListComponent},
];
@NgModule({
imports: [
BrowserModule,
FormsModule,
RouterModule.forRoot(
appRoutes,
{enableTracing: true} // <-- debugging purposes only
)
],
declarations: [
AppComponent,
HeroListComponent,
CrisisListComponent,
],
bootstrap: [AppComponent]
})
export class AppModule {}
Registering the RouterModule.forRoot() in the AppModule imports array makes the Router service available everywhere in the application.
在 AppModule imports 数组里注册 RouterModule.forRoot 的返回后果,确保 Router 服务在利用的任意地位都能被应用。
The root AppComponent is the application shell. It has a title, a navigation bar with two links, and a router outlet where the router renders components.
The router outlet serves as a placeholder where the routed components are rendered.
router outlet 就是一个占位符,用来寄存被路由的 Component.
Add a wildcard route to intercept invalid URLs and handle them gracefully. A wildcard route has a path consisting of two asterisks. It matches every URL. Thus, the router selects this wildcard route if it can’t match a route earlier in the configuration. A wildcard route can navigate to a custom “404 Not Found” component or redirect to an existing route.
wildcard route 就是一个优雅的路由错误处理机制。
{path: '**', component: PageNotFoundComponent}
如何设置默认路由?
应用 redirectTo 属性:
const appRoutes: Routes = [{ path: 'crisis-center', component: CrisisListComponent},
{path: 'heroes', component: HeroListComponent},
{path: '', redirectTo:'/heroes', pathMatch:'full'},
{path: '**', component: PageNotFoundComponent}
];
应用 Angular CLI 创立启用了 routing 性能的 Component:
ng generate module my-module –routing
This tells the CLI to include the @angular/router npm package and create a file named app-routing.module.ts. You can then use routing in any NgModule that you add to the project or app.
const appRoutes: Routes = [{ path: 'crisis-center', component: CrisisListComponent},
{path: 'heroes', component: HeroListComponent},
{path: '', redirectTo:'/heroes', pathMatch:'full'},
{path: '**', component: PageNotFoundComponent}
];
@NgModule({
imports: [
RouterModule.forRoot(
appRoutes,
{enableTracing: true} // <-- debugging purposes only
)
],
exports: [RouterModule]
})
export class AppRoutingModule {}
路由时的参数传递
<a [routerLink]="['/hero', hero.id]">
路由 url:localhost:4200/hero/15.
The router extracts the route parameter (id:15) from the URL and supplies it to the HeroDetailComponent via the ActivatedRoute service.
Router 将路由参数 id:15 从 url 里提取进去,通过 ActivatedRoute 服务传递到路由的目标 Component 中去。
如何应用 ActivatedRoute
constructor(
private route: ActivatedRoute,
private router: Router,
private service: HeroService
) {}
从 ActivatedRoute 中提取出参数 id:
ngOnInit() {
this.hero$ = this.route.paramMap.pipe(switchMap((params: ParamMap) =>
this.service.getHero(params.get('id')))
);
}
When the map changes, paramMap gets the id parameter from the changed parameters.
当参数 map 发生变化时,下面代码的 paramMap 从变动的参数里取得 id 参数。
The switchMap operator does two things. It flattens the Observable<Hero> that HeroService returns and cancels previous pending requests. If the user re-navigates to this route with a new id while the HeroService is still retrieving the old id, switchMap discards that old request and returns the hero for the new id.
switchMap 操作符做的两件事件:将 Observable\<hero> 的返回类型,平坦化为 Hero,同时 cancel 之前 pending 的申请。如果用户从新跳转到这条路由门路,而 HeroService 依然在读取前一个 id,则 old 的申请被 discard.
为什么要用 Observable 包裹 Param?
In this example, you retrieve the route parameter map from an Observable. That implies that the route parameter map can change during the lifetime of this component.
暗示了存储路由参数的 map 有可能在该 Component 生命周期内发生变化。
By default, the router re-uses a component instance when it re-navigates to the same component type without visiting a different component first. The route parameters could change each time.
默认状况下,当咱们重复跳转到一个同样的 UI 时,router重用 该 UI Component 实例。
You wouldn’t want the router to remove the current HeroDetailComponent instance from the DOM only to re-create it for the next id as this would re-render the view. For better UX, the router re-uses the same component instance and updates the parameter.
Router 重用 Component 实例,只是替换 parameter 值。
Since ngOnInit() is only called once per component instantiation, you can detect when the route parameters change from within the same instance using the observable paramMap property.
ngOnInit 在 Component 整个生命周期里只会触发一次,所以咱们能够用 Observable 包裹过的 paramMap 属性,来检测参数值的变动。
This application won’t re-use the HeroDetailComponent. The user always returns to the hero list to select another hero to view. There’s no way to navigate from one hero detail to another hero detail without visiting the list component in between. Therefore, the router creates a new HeroDetailComponent instance every time.
如果是 list-detail 格调的利用,咱们无奈从一个明细页面跳转到另一个明细页面,两头必须通过 list 页面的直达。因而,router 每次被迫创立新的明细页面 Component 实例。
When you know for certain that a HeroDetailComponent instance will never be re-used, you can use snapshot.
route.snapshot provides the initial value of the route parameter map. You can access the parameters directly without subscribing or adding observable operators as in the following:
从 route.snapshot 能获取 route 参数的初始值。
ngOnInit() {const id = this.route.snapshot.paramMap.get('id');
this.hero$ = this.service.getHero(id);
}
snapshot only gets the initial value of the parameter map with this technique. Use the observable paramMap approach if there’s a possibility that the router could re-use the component. This tutorial sample app uses with the observable paramMap.
如果 router 会重用一个 Component,这意味着 paramMap 在 Component 生命周期会发生变化,此时要用 Observable 包裹后的 paramMap 来检测这种变动。
为什么咱们须要 Route Guard
- Perhaps the user is not authorized to navigate to the target component.
- Maybe the user must login (authenticate) first.
- Maybe you should fetch some data before you display the target component.
- You might want to save pending changes before leaving a component.
- You might ask the user if it’s OK to discard pending changes rather than save them.
通过路由守卫的返回值确定路由是否持续。
- If it returns true, the navigation process continues.
- If it returns false, the navigation process stops and the user stays put.
- If it returns a UrlTree, the current navigation cancels and a new navigation is initiated to the UrlTree returned.
CanActivate: requiring authentication
Applications often restrict access to a feature area based on who the user is. You could permit access only to authenticated users or to users with a specific role. You might block or limit access until the user’s account is activated.
The CanActivate guard is the tool to manage these navigation business rules.
用于实现权限查看。
看一个例子:
实现一个 AuthGuard:
import {Injectable} from '@angular/core';
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '@angular/router';
@Injectable({providedIn: 'root',})
export class AuthGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean {console.log('AuthGuard#canActivate called');
return true;
}
}
在 app module 里导入这个 AuthGuard,保护到 Routes 数组元素的 canActivate 属性里:
import {AuthGuard} from '../auth/auth.guard';
const adminRoutes: Routes = [
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard],
children: [
{
path: '',
children: [{ path: 'crises', component: ManageCrisesComponent},
{path: 'heroes', component: ManageHeroesComponent},
{path: '', component: AdminDashboardComponent}
],
}
]
}
];
@NgModule({
imports: [RouterModule.forChild(adminRoutes)
],
exports: [RouterModule]
})
export class AdminRoutingModule {}
一个模仿登录的 service:
import {Injectable} from '@angular/core';
import {Observable, of} from 'rxjs';
import {tap, delay} from 'rxjs/operators';
@Injectable({providedIn: 'root',})
export class AuthService {
isLoggedIn = false;
// store the URL so we can redirect after logging in
redirectUrl: string;
login(): Observable<boolean> {return of(true).pipe(delay(1000),
tap(val => this.isLoggedIn = true)
);
}
logout(): void {this.isLoggedIn = false;}
}
The ActivatedRouteSnapshot contains the future route that will be activated and the RouterStateSnapshot contains the future RouterState of the application, should you pass through the guard check.
If the user is not logged in, you store the attempted URL the user came from using the RouterStateSnapshot.url and tell the router to redirect to a login page—a page you haven’t created yet. Returning a UrlTree tells the Router to cancel the current navigation and schedule a new one to redirect the user.
更多 Jerry 的原创文章,尽在:” 汪子熙 ”: