angular2 学习笔记 ( Router 路由 )
更新 : 2019-11-21
我们经常喜欢用 empty string 来做 default router path
比如 2 个 tabs
<nav mat-tab-nav-bar>
<a mat-tab-link
[routerLink]="['./']" routerLinkActive [routerLinkActiveOptions]="{exact: true}" #rla1="routerLinkActive" [active]="rla1.isActive">
Test A {{ rla1.isActive }}
</a> <a mat-tab-link
[routerLink]="['test-b']" routerLinkActive #rla2="routerLinkActive" [active]="rla2.isActive">
Test B {{ rla2.isActive }}
</a>
</nav>
要留意第一个如果忘记放 exact: true, 那么第二个被匹配到的时候它也会 active 哦,所以使用 empty path + router active 时要记得了
注意 : router link 配 empty string 还要有 relation 写法是
<a routerLink=“./” >go</a>
更新: 2019-08-05
之前都没有什么用到 matrix url, 因为项目难度不大,用 queryparams 挺好的,但是越来月复杂后开始感觉到 matrix 的好了.
要改动 matrix 比较难, 没有类似 'merge' 啊等等的东西.
常用的方式是
<a [routerLink]="['../',{}]" >gogo</a>
不过要留意哦, root path 就不可以用这招了,root path should not have matrix.
然后说到 ../ 我就气!
https://github.com/angular/angular/issues/13011 这个 issue 几年前我就 like 了. 然后我也不记得那时候怎样闪掉了.
今天又给我与到... 我根本就不记得有过这种事情了... n 年前... 尽然没有 fix !!! 什么鬼嘛
看了几眼原来是有 fix 的.. 只是 fix 的不全面而已
relativeLinkResolution?: 'legacy' | 'corrected' 就是用来解决这类问题的 (问题不只一种)
幸好我这次遇到的可以用这个 fix. 希望不要再有问题了。
Angular 的 Router 是真的很强, 但是也很多坑 ! 一堆问题没人理.. 唉..
更新 : 2018-7-25
Base href 的奇妙
默认情况下, ng 会为每个项目加上一个 base href="/"
<base href="/">
依据规范, base href 的结尾必须是 slash "/" 比如 "/en/", "/cn/"
看看下面的情况
可以获取图
<base href="/">
<img src="/images/stickman.gif" width="24" height="39"> 可以获取图
<base href="/">
<img src="data:images/stickman.gif" width="24" height="39"> 不可以获取图
<base href="/images/">
<img src="/stickman.gif" width="24" height="39"> 可以获取图
<base href="/images/">
<img src="stickman.gif" width="24" height="39">
是不是有点奇葩... 反正 best pratice 就是一定要写 base href, 如果没有特别的就放 "/"
所有路径都不要使用 slash "/" 作为开始, 这样就安全了.
要在 ng 里获取 base href 的值可以注入服务 PlatformLocation.getBaseHrefFromDOM()
更新 : 2017-12-19
今天遇到一个 关于 router empty path + relative 的 bug
https://github.com/angular/angular/issues/18059
https://github.com/angular/angular/issues/13011
https://github.com/angular/angular/issues/17957
也可能这个不是 bug 是 ng 的设计思路.
总只能结果就是如果你想在 component 内使用 relative routerLink 比如 "../" or "./" or "child-path"
那么请你确保你这个 component 所在的 router 一定要有 parent path. 如果所有 parent path 都是 empty string 那么你就 gg.com 了.
imports: [RouterModule.forRoot([
{ path: '', pathMatch: 'full', redirectTo: '/products' },
{
path: '',
children: [
{
path: '',
component: HomeComponent,
children: [{
path: '',
component: CategoryComponent, // <a routerLink="products" >category go</a> 《--result is products/(products) ....
children: [
{
path: 'products',
component: ProductComponent
}
]
}]
}
]
}
])],
下面这个就 ok
imports: [RouterModule.forRoot([
{ path: '', pathMatch: 'full', redirectTo: 'dada/products' },
{
path: '',
children: [
{
path: 'dada', // 要有 parent path
component: HomeComponent,
children: [{
path: '',
component: CategoryComponent, // <a routerLink="products" >category go</a> OK !
children: [
{
path: 'products',
component: ProductComponent
}
]
}]
}
]
}
])],
更新 : 2017-11-04
lazy load vs common chunk
by default ng 会把共用的模块放入 common chunk 里头, 确保即使在 lazy load 的情况下, 模块都不会被加载 2 次 (不重复)
但是这样的设置并不一定就最好的,因为 lazy load 的目的本来就是最小化的加载丫.
这只是一个平衡的问题. ng 视乎只给了 2 个极端的做法, 要嘛使用 common chunk 要嘛完全不要 common chunk
ng build --prod --no-common-chunk
更新 : 2017-10-18
angular 的 router 有一个原则, 如果你触发一个 <a href> 或则调用 router.navigate(...) 但是最终它发现 url 没变动,那么什么不会发生, route event 统统没有运行.
还有另一个是当 url change 时 angular 不会轻易 rebuild component, 如果它的 path 依然是激活的 angular 会保留它哦.
更新 : 2017-08-04
今天我才发现其实 Preload 不像我说的那样, 我们可以在 preload 方法中把 load 这个方法保持起来.
export class AppCustomPreloader implements PreloadingStrategy {
loaders: Function[] = [];
preload(route: Route, load: Function): Observable<any> {
this.loaders.push(load);
return Observable.of(null);
}
}
然后在任何一个 component 注入 AppCustomePreloader 并调用 load 就可以了.
ng 是很聪明的,如果 load() 运行时发现其模块已经加载了, 那么它并不会报错. 所以鼓励大家去使用它 .
更新 : 2017-04-05
this.router.navigate(['../../'], { relativeTo: this.activatedRoute, queryParamsHandling: 'preserve' });
<a routerLink="../../" queryParamsHandling="preserve">
queryParamsHandling and preserveFragment 可以在移动 router 时保留当前的 queryParams and fragment 很方便哦。
queryParamsHandling 不只是可以 preserve, 还可以 merge 哦
更新 : 2017-03-27
matcher
如果我想自己写 url 匹配, 我们可以通过 matcher
export function matcher(segments: UrlSegment[], group: UrlSegmentGroup, route: Route) {
//判断
return {
consumed: segments.slice(1), //如果你的 route 还有 child 的话,这里要注意,只放入你所匹配到的范围,后面的交给 child 去判断.
posParams: { Id: segments[1] } // 传入 params, url matrix 等等
}
} @NgModule({
imports: [RouterModule.forChild([
{
matcher : matcher, //帮发放放进来, 这里不要使用匿名方法或则箭头函数哦, aot 不过
component : FirstComponent,
children : []
}
])],
exports: [RouterModule],
})
export class DebugRoutingModule { }
更新 2017-03-05
preloading module
lazy load 的好处是 first load 很快, 因为很多 module 都没有 load 嘛, 但是后续的操作就会变成卡卡的, 因为后来要 load 嘛.
2.1 开始 ng 支持 preloading 的概念. 就是通过 lazyload 让你 firstload 很快之后, 立马去预先加载其它的 module.
比如你的 first page 是一个登入界面, 用户就可以很快看到页面,让后乘着客户在输入密码时,你偷偷去加载后续会用到的模块。这样客户接下来的操作就不会卡卡的了.
这听上去不错哦.
要注意的是, ng 并不太智能, 它不会等到 browser idle 的时候才去加载. 它会马上去加载.
如果你的首页有很多图片或者视屏, ng 不会等待这些图片视屏加载完了才去加载其它模块, 它会马上去加载. 这可能会造成一些麻烦 (因项目而定, 自己做平衡哦)
@Injectable()
export class MyPreloadingStrategy implements PreloadingStrategy {
constructor(private route : ActivatedRoute, private router : Router) {
//可以注入 router route 任何 service 来帮助我们判断也不要 preload
}
preload(route: Route, load: () => Observable<any>): Observable<any> {
// ng 会把每一个 lazyload 的 module 丢进来这个函数, 问问你是否要 preload, 如果要, 你就返回 load() 不要 preload 的话就返回 Observable.of(null);
return (true) ? load() : Observable.of(null);
}
} @NgModule({
imports: [RouterModule.forRoot([
{
path: 'home',
loadChildren: "app/+home/home.module#HomeModule"
},
{
path: "about",
loadChildren: "app/+about/about.module#AboutModule"
},
{
path: "contact",
loadChildren: "app/+contact/contact.module#ContactModule"
},
{
path: "",
redirectTo: "home",
pathMatch: "full"
}
], { preloadingStrategy: MyPreloadingStrategy })], // { preloadingStrategy: PreloadAllModules } <--ng 自带的
exports: [RouterModule], providers: [MyPreloadingStrategy] }) export class AppRoutingModule { }
只要在 forRoot 里添加 preloadingStrategy 就可以了. 上面我用了一个自定义的处理, 如果你想简单的表示全部都预加载的话,可以使用 ng 提供的 PreloadAllModules
更新 2017-01-29
提醒 :
路由是有顺序的, 在用 import 特性模块时, 位置要留意.
例如, 如果你 app-routing 最后是处理 404
但是在 app-module 却把 routing 限于特性模块 IdentityModule, 那么 IdentityModule 的 routing 就进不去了。因为已经被匹配掉了.
2016-08-26
参考 :
https://angular.cn/docs/ts/latest/guide/router.html#!#can-activate-guard
https://angular.cn/docs/ts/latest/api/ -@angular/router 部分
ng 路由的概念和游览器类似, 和 ui-router 也类似, 下面会把具体功能逐一解释
1. html5 和 hash #
ng 默认模式是 html5, 在开发阶段我们喜欢使用 hash 模式, 这样可以不用部署服务器.
要从 html5 转换成 hash 只要做一个小设定 :
(update:用 angular cli 开发的话,不需要 hash 模式了.)
2.child
和 ui-view 一样 ng 也支持嵌套
就是一个路由的组件模板内又有另一个路由组件
const appRoutes: Routes = [
{
path: "",
redirectTo: "home",
pathMatch: "full"
},
{
path: "home",
component: TopViewComponent, //view 内也有 <router-outlet>
children: [
{
path: "" //如果没有设置一个空路由的话, "/home" 会报错, 一定要 "/home/detail" 才行.
},
{
path: "detail",
component: FirstChildViewComponent
}
]
}
];
3. 获取 params ( params 是 Matrix Url 和 :Id , 要拿 search 的话用 queryParams )
class TestComponent implements OnInit, OnDestroy {
//home/xx
private sub : Subscription;
constructor(private route: ActivatedRoute) { }
ngOnInit()
{
//监听变化
this.sub = this.route.params.subscribe(params => {
console.log(params); //{ id : "xx" }
});
//如果只是要 get 一次 value, 用快照
console.log(this.route.snapshot.params); //{ id : "xx" }
}
ngOnDestroy()
{
this.sub.unsubscribe(); //记得要取消订阅, 防止内存泄露 (更新 : 其实 ActivatedRoute 可以不需要 unsubscribe,这一个 ng 会智能处理,不过养成取消订阅的习惯也是很好的)
}
}
4. 导航
导航有个重要概念, 类似于游览器, 当我们表示导航时 ../path, /path, path 它是配合当下的区域而做出相对反应的. 记得是 path+区域 哦.
<a [routerLink]="['data',{ key : 'value' }]" [queryParams]="{ name : 'keatkeat' }" fragment="someWhere" >go child</a>
export class TopViewComponent {
constructor(private router: Router, private route: ActivatedRoute) {
console.clear();
}
click(): void {
this.router.navigate(
["data", { key: "value" }], //data 是 child path, {key : "value"} 是 Matrix Url (矩阵 URL) 长这样 /data;key=value
{
relativeTo: this.route, //表示从当前route开始, 这个只有在 path not start with / 的情况下需要放.
//一般的 queryParams, 这里只能 override 整个对象, 如果你只是想添加一个的话,你必须自己实现保留之前的全部.
queryParams: {
'name': "keatkeat" // ng 会对值调用 toString + encode 才放入 url 中, 解析时会 decode, 然后我们自己把 str convert to 我们要的值
},
//#坐标
fragment: "someWhere",
replaceUrl : true //有时候我们希望 replace history 而不是 push history
}
);
//redirect by url
let redirectUrl = this.route.snapshot.queryParams["redirectUrl"];
if (redirectUrl != undefined) {
this.router.navigateByUrl(redirectUrl);
}
else {
this.router.navigate(["/admin"]);
}
}
}
大概长这样, 最终个结果 : /home/data;key=value?name=keatkeat#someWhere
通过指令 routerLink , 或则使用代码都可以 (写法有一点点的不同, 看上面对比一下吧)
导航使用的是数组, ["path1","path2",{ matrix : "xx" },"path3"], 你也不一定要一个 path 一个格子, ['/debug/a/b','c'] 也是 work 的
和游览器类似
"/path" 表示从 root 开始
"../path" 表示从当前route往上(parent)
"path" 表示从当前往下(child)
这里有个关键概念, 在不同的 component 获取到的 this.route 是不同的, 组件和 route 是配合使用的
比如上面 click() 方法,如果你放在另一个组件,结果就不同了,this.route 会随着组件和改变.
没有 route name 的概念 (之前好像是有,不知道是不是改了..没找到../.\), 就是用 path 来操作.
matrix url 和 params 获取的手法是一样的. 他的好处就是不需要把所有子孙层页面的参数都放到 params 中,放到 matrix url 才对嘛.
提醒 : { path : "product?name&age" } 注册 route 的时候不要定义 queryParam. ?name&age 删掉 (ui-router need, ng not)
5. 拦截进出
通常进入时需要认证身份,出去时需要提醒保存资料.
ng 为我们提供了拦截点
{
path: ":id",
component: TestComponent,
data: {
title : "test"
},
canActivate: [BlockIn], //进入
canDeactivate : [BlockOut] //出去
}
BlockIn, BlockOut 分别是2个服务
@Injectable()
export class BlockIn implements CanActivate {
constructor(
private currentRoute: ActivatedRoute,
private router: Router,
) { }
canActivate(nextRoute: ActivatedRouteSnapshot, nextState: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
//this.currentRoute vs nextRoute some logiz
let nextUrl = nextState.url;
let currentUrl = this.router.url;
return new Promise<Boolean>((resolve, reject) => {
setTimeout(() => {
resolve(true);
},5000);
});
}
}
实现了 CanActivate 接口方法, 里头我们可以获取到即将进入的路由, 这样就足够让我们做验证了, 途中如果要跳转页是可以得哦..
@Injectable()
export class BlockOut implements CanDeactivate<TestComponent> { canDeactivate(
component: TestComponent,
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean> | boolean {
console.log("leave");
return true;
}
}
CanDeactivate 还多了一个 Component, 让我们在切出的时候可以调用 component 内容来做检查或者保存资料等等, 很方便呢.
还有一个叫 CanActivateChild, 依据子层来决定能否访问... 它和 CanActivate 的区别是
canActivate(nextRoute: ActivatedRouteSnapshot) vs canActivateChild(childRoute: ActivatedRouteSnapshot)
有点像 dom 事件的 event.target vs event.currenttarget 的概念.
6. resolve
@Injectable()
export class DataResolve implements Resolve<String> {
constructor(private router: Router) { }
resolve(route: ActivatedRouteSnapshot): Observable<any> | Promise<any> | any {
console.log("masuk");
if ("xx" === "xx") {
return "xx";
}
else
{
this.router.navigate(['/someWhere']); //随时可以跳转哦
}
}
}
{
path: "home",
component: TopViewComponent,
resolve: {
resolveData: DataResolve
},
}
注册在 route 里
providers: [appRoutingProviders, Title, BlockIn, BlockOut, DataResolve],
还要记得注册服务哦
ngOnInit()
{
console.log("here");
console.log(this.route.snapshot.data);
}
在 onInit 里面就可以使用啦.
提醒 : 和 ui-router 不同的时, ng 的 resolve 和 data 是不会渗透进子路由的,但是我们在子路由里调用 this.route.parent.... 来获取我们想要的资料.
7. set web browser title
这个和 router 没有直接关系,只是我们通常会在还 route 时改动 browser title 所以记入在这里吧
ng 提供了一个 service 来处理这个title
import { BrowserModule, Title } from '@angular/platform-browser'; providers: [appRoutingProviders, Title, BlockIn, BlockOut, DataResolve] constructor(private route: ActivatedRoute, private titleService: Title) {
this.titleService.setTitle("data");
}
注册 & 注入 service 就可以用了
8.auxiliary routes / multi view
参考 :
const appRoutes: Routes = [
{
path: "home",
children: [
{
path: "detail",
},
{
path: "popup",
outlet : "popup"
}
]
},
{
path: "chat",
outlet : "chat"
}
];
结构提醒 :
根层是 /home(chat:chat) 而不是 /(home//chat:chat)
子层是 /home/(detail//popup:popup) 而不是 /home/detail(popup:popup)
要留意哦.
// create /team/33/(user/11//aux:chat)
router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: 'chat'}}]);
// remove the right secondary node
router.createUrlTree(['/team', 33, {outlets: {primary: 'user/11', right: null}}]);
生成的方式.
9.异步加载特性模块
首先特性模块和主模块的 routing 设置不同
一个用 .forRoot, 一个用 .forChild 方法
export const routing: ModuleWithProviders = RouterModule.forChild(routes);
要异步加载特性模块的话,非常简单.
在主路由填入 loadChildren 属性,值是模块的路径#类的名字
const appRoutes: Routes = [
{
path: "",
redirectTo: "/home",
pathMatch: "full"
},
{
path: "home",
component: HomeComponent
},
{
path: "product",
loadChildren: "app/product/product.module#ProductModule" //ProductModule 是类的名字, 如果是用 export default 的话,这里可以不需要表明
}
];
在特性模块的路由, 把 path 设置成空
const routes: Routes = [
{
path: "",
component: ProductComponent
}
];
这样就可以啦.
小记:
1.Router : 用于 redirect 操作
2.ActivateRoute : 用于获取 data, params 等等
3.Route : 就是我们每次注册时写的资料咯, 里面有 data, path 哦
angular2 学习笔记 ( Router 路由 )的更多相关文章
- Asp.net core 学习笔记 ( Router 路由 )
和之前的一样用法. public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory log ...
- vue2.0学习笔记之路由(二)路由嵌套+动画
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue2.0学习笔记之路由(二)路由嵌套
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...
- vue学习笔记(十)路由
前言 在上一篇博客vue学习笔记(九)vue-cli中的组件通信内容中,我们学习组件通信的相关内容和进行了一些组件通信的小练习,相信大家已经掌握了vue-cli中的组件通信,而本篇博客将会带你更上一层 ...
- Tornado学习笔记(二) 路由/post/get传参
本章我们学习 Tornado 的路由传参等问题 路由 路由的匹配 Tornado的路由匹配采用的是正则匹配 一般情况下不需要多复杂的正则,正则的基本规则如下(站长之家) 举个例子 (r'/sum/(\ ...
- vue常用操作及学习笔记(路由跳转及路由传参篇)
路由跳转 - 超链接方式跳转 html: <div id="app"> <h1>Hello App!</h1> <p> <!- ...
- Angular2学习笔记——路由器模型(Router)
Angular2以组件化的视角来看待web应用,使用Angular2开发的web应用,就是一棵组件树.组件大致分为两类:一类是如list.table这种通放之四海而皆准的通用组件,一类是专为业务开发的 ...
- Angular2学习笔记——在子组件中拿到路由参数
工作中碰到的问题,特此记录一下. Angular2中允许我们以`path\:id\childPath`的形式来定义路由,比如: export const appRoutes: RouterConfig ...
- angular2 学习笔记 ( ngModule 模块 )
2016-08-25, 当前版本是 RC 5. 参考 : https://angular.cn/docs/ts/latest/guide/ngmodule.html 提醒 : 这系列笔记的 " ...
随机推荐
- Android开发之从网络URL上下载JSON数据
网络下载拉取数据中,json数据是一种格式化的xml数据,非常轻量方便,效率高,体验好等优点,下面就android中如何从给定的url下载json数据给予解析: 主要使用http请求方法,并用到Htt ...
- Java client 访问 memcached
在测试项目中引入了memcached作为缓存层,以下是memcached的缓存配置和调用过程. linux下memcached安装过程 直接参考以前的博文linux下安装memcached过程 不再 ...
- C++ STL (备忘)
2014-08-04 16:33:57 (1) map map定义形式 map<type1,type2> map_name; map的基本操作函数: C++ Maps是一种关 ...
- 第十一篇:web之Django之Form组件
Django之Form组件 Django之Form组件 本节内容 基本使用 form中字段和插件 自定义验证规则 动态加载数据到form中 1. 基本使用 django中的Form组件有以下几个功 ...
- jquery操作iframe中的js函数
关键字:jquery操作iframe中的js函数 1.jquery操作iframe中的元素(2种方式) var tha = $(window.frames["core_content&quo ...
- java Spring 在WEB应用中的实例化
.前面讲解的都是通过直接读取配置文件,进行的实例化ApplicationContext AbstractApplicationContext app = new ClassPathXmlApplica ...
- 淘宝开源Web服务器Tengine安装教程
简介Tengine是由淘宝核心系统部基于Nginx开发的Web服务器,它在Nginx的基础上,针对大访问量网站的需求,添加了很多功能和特性.Tengine的性能和稳定性已经在大型的网站如淘宝网,淘宝商 ...
- OC - 4.OC核心语法
一.点语法 1> 基本使用 点语法本质上是set方法/get方法的调用 2> 使用注意 若出现在赋值操作符的右边,在执行时会转换成get方法 若出现在赋值操作符的左边,在执行时会转换成se ...
- 插入排序之python实现源码
def insert_sort(old): for i in range(1, len(old)): for j in range(i, 0, -1): if(old[j] < old[j-1] ...
- mysql远程连接缓慢的问题
这两天发现服务器程序启动的时候到了mysql初始连接的那一步很耗时,启动缓慢,后来发现,将连接的主机的-h参数改成localhost的时候 瞬间就完成连接了.后来在网上查到,原来是由于mysql服务器 ...