SPA
single page web application,单页Web应用
视图状态列表集合
不重新加载页面
路由知识
1.创建项目
–>ng new router --routing
2.生成组件
–>ng g component home
–>ng g component product
2.1Routes路由配置
app-routing.module.ts
import {ProductComponent} from './product/product.component';
const routes: Routes = [
/*根路由*/
{path: '', component: HomeComponent},
{path: 'product', component: ProductComponent}
];
2.2 RouterOutlet路由插座
app.component.html
<router-outlet></router-outlet>
2.3 RouterLink路由导航
app.component.html
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/product']">商品详情</a>
注:参数是数组,路由传参
运行看效果:–> npm run start
2.4 Router路由导航
app.component.html
<!--(click):事件绑定-->
<input type="button" value="商品详情" (click)="toProductDetails()">
app.component.ts
import {Router} from '@angular/router';
export class AppComponent {
title = 'router';
/*获得router对象*/
constructor(private router: Router) {
}
toProductDetails() {
/*通过router对象navigate方法导航*/
this.router.navigate(['/product']);
}
}
问题
问题:url输入路径不存在,报错
解决:
1.–> ng g component code404
2.app-routing.module.ts
import {Code404Component} from './code404/code404.component';
const routes: Routes = [
/*根路由*/
{path: '', component: HomeComponent},
{path: 'product', component: ProductComponent},
{path: '**', component: Code404Component}
];
注:通配符路由最后配置
2.5 ActivatedRoute
2.5.1在路由时传递数据:
1.在查询参数中传递数据
/product?id=1&name=2
=> ActivatedRoute.queryParams[id]
app.component.html
<a [routerLink]="['/product']" [queryParams]="{id: 1}">商品详情</a>
product.component.ts
import {ActivatedRoute} from '@angular/router';
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute) { }
ngOnInit() {
this.productId = this.routeInfo.snapshot.queryParams['id'];
}
}
product.component.html
<p>
商品ID是{{productId}}
</p>
浏览器点击显示:http://localhost:4200/product?id=1
2.在路由路径中传递数据
{path: ‘product/:id’ }
=> /product/1
=> ActivatedRoute.params[id]
app.component.html
<a [routerLink]="['/product', 1]">商品详情</a>
product.component.ts
import {ActivatedRoute} from '@angular/router';
export class ProductComponent implements OnInit {
private productId: number;
constructor(private routeInfo: ActivatedRoute) { }
ngOnInit() {
//this.productId = this.routeInfo.snapshot.queryParams['id'];
this.productId = this.routeInfo.snapshot.params['id'];
}
}
浏览器点击显示:http://localhost:4200/product/1
3.在路由配置中传递数据
{path: product,component: ProductComponent , data [{isProd:true}]}
=> ActivatedRoute.data[0][isProd]
问题
修改app.component.ts
import {Router} from '@angular/router';
export class AppComponent {
...
toProductDetails() {
this.router.navigate(['/product', 2]); }
}
问题:页面点击商品详情链接和按钮,url改变,html展示不变
原因:商品详情组件不会再次创建,ngOnInit()方法不会再次执行
解决:修改product.component.ts
参数快照:
this.productId = this.routeInfo.snapshot.params['id'];
参数订阅:
this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']);
3.重定向路由
修改app-routing.module.ts
const routes: Routes = [
/*重定向*/
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
/*{path: 'product', component: ProductComponent},*/
{path: 'product/:id', component: ProductComponent},
{path: '**', component: Code404Component}
];
4.子路由
4.1添加商品详情子组件
商品描述:–>类似home组件
-> ng g component productDesc
销售员信息:–>类似product组件
-> ng g component sellerInfo
4.2商品详情子路由配置
product.component.html
<a [routerLink]="['./']">商品描述</a>
<a [routerLink]="['./seller', 99]">销售员信息</a>
<router-outlet></router-outlet>
app-routing.module.ts
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
const routes: Routes = [
/*根路由*/
{path: '', redirectTo: '/home', pathMatch: 'full'},
{path: 'home', component: HomeComponent},
{path: 'product/:id', component: ProductComponent, children: [
/*子路由*/
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
]},
{path: '**', component: Code404Component}
];
5.辅助路由
1.路由插座
辅助插座 ,
app.component.html
<router-outlet name="aux"></router-outlet>
2.生成聊天组件
-> ng g component chat
chat.component.html
<textarea placeholder='请输入聊天内容'></textarea>
3.路由配置
app-routing.module.ts
const routes: Routes = [
/*根路由*/
{path: '', redirectTo: '/home', pathMatch: 'full'},
/*添加chat路由配置*/
{path: 'chat',component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
{path: 'product/:id', component: ProductComponent, children: [
/*子路由*/
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
]},
{path: '**', component: Code404Component}
];
4.路由导航
app.component.html
<a [routerLink]="[{outlets: {aux: 'chat'}}]">开始聊天</a>
<a [routerLink]="[{outlets: {aux: null}}]">结束聊天</a>
追加:主路由没有name,使用primary指定
<a [routerLink]="[{outlets: {primary: ‘home’, aux: ‘chat’}}]">开始聊天
6.路由守卫
路由守卫:
- CanActivate : 处理导航到某路由的情况
- CanDeactivate: 处理从当前路由离开的情况
- Resolve:在路由激活之前获取路由数据
使用场景:
1.用户登录并拥有某些权限–》进入某些路由
app下新建目录:guard
guid下新建文件:login.guard.ts
login.guard.ts
import {CanActivate} from '@angular/router';
export class LoginGuard implements CanActivate{
canActivate() {
let loggedIn: boolean = Math.random()<0.5;
if(!loggedIn) {
console.log("用户未登录");
}
return loggedIn;
}
}
app-routing.module.ts
const routes: Routes = [
/*根路由*/
{path: '', redirectTo: '/home', pathMatch: 'full'},
/*添加chat路由配置*/
{path: 'chat',component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
/*添加路由守卫配置*/
{path: 'product/:id', component: ProductComponent, children: [
/*子路由*/
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
], canActivate: [LoginGuard]},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
/*声明LoginGuard*/
providers: [LoginGuard]
})
注:
只是指定路由守卫的类型,未实例化LoginGuard
Angular采用依赖注入方式实例化LoginGuard
2.用户未执行保存操作而试图离开当前导航时提醒用户
guid下新建文件:unsaved.guard.ts
unsaved.guard.ts
import {CanDeactivate} from '@angular/router';
import {ProductComponenent} from '../product/product.component';
export class UnsavedGuard implements CanDeactivate<ProductComponenent>{
canDeactivate(componenent: ProductComponenent) {
return window.confirm("未保存,离开?");
}
}
app-routing.module.ts
const routes: Routes = [
/*根路由*/
{path: '', redirectTo: '/home', pathMatch: 'full'},
/*添加chat路由配置*/
{path: 'chat',component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
/*添加路由守卫配置*/
{path: 'product/:id', component: ProductComponent, children: [
/*子路由*/
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
], canActivate: [LoginGuard],
canDeactivate: [UnsavedGuard] },
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
/*声明UnsavedGuard*/
providers: [LoginGuard, UnsavedGuard]
})
3.有多个表单组件组成的向导:如
注册流程–》用户只有在当前路由的组件中填写满足要求的信息才可以导航到下一个路由
7.resolve守卫
解决问题:
before:项目使用http获取数据有延时,模板数据加载问题,用户体验差
after:在进入组件之前,把组件所需数据全加载好,如果出错–》弹出,不进入目标路由
guid下新建文件:product.resolve.ts
product.resolve.ts
import {Resolve} from '@angular/router';
import {Product} from '../product/product.component';
import {Injectable} from '@angular/core';
/*<Product>: resolve解析出来数据类型*/
@Injectable
/*装饰器,constructor中的router才能被注入进来
*component不需要,已经继承装饰器
*/
export class ProductResolve implements Resolve<Product>{
constructor(private router: RouterStateSnapshot) {}
resolve(route: ActivatedRouteSnapshot, state: RouteStateSnapshot: Observable<Product>|Promise<Product>) {
let productId : number;
productId = router.params['id'];
if(productId == 1) {
return new Product(1, 'iphone7');
}else{
this.router.navigate(['/home']);
return undefined;
}
}
}
product.component.ts
export class ProductComponent implements OnInit {
private productId: number;
private productName: string;
...
ngOnInit() {
this...
this.routeInfo.data.subscribe((data: {product: Product}) => {
this.productId = data.product.id;
this.productName = data.product.name;
});
}
}
export class Product {
constructor(public id: number, public name: string) {}
}
app-routing.module.ts
const routes: Routes = [
/*根路由*/
{path: '', redirectTo: '/home', pathMatch: 'full'},
/*添加chat路由配置*/
{path: 'chat',component: ChatComponent, outlet: 'aux'},
{path: 'home', component: HomeComponent},
/*添加resolve守卫配置*/
{path: 'product/:id', component: ProductComponent, children: [
/*子路由*/
{path: '', component: ProductDescComponent},
{path: 'seller/:id', component: SellerInfoComponent}
], resolve: {
product: ProductResolved
}
},
{path: '**', component: Code404Component}
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
/*声明ProductResolved*/
providers: [LoginGuard, UnsavedGuard, ProductResolved]
})
product.component.html
<p>商品名称是: {{productName}}</p>