【6】 angular 路由基础知识_在路由时传递数据_重定向路由_子路由_辅助路由_路由守卫

版权声明:独学而无友,则孤陋寡闻。q群582951247 https://blog.csdn.net/mp624183768/article/details/82855667

路由基础知识

  路由相关对象介绍

 

新建一个项目

ng new router --routing

并使用webstrom 打开 

发现--routing 参数创建后 会多出一个文件app-routing.module.ts 就是当前应用的路径配置

内容如下

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

const routes: Routes = [];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }

app.module.ts 也会导入这个模块

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.html 下面也会多一个插座

<router-outlet></router-outlet>

为了展示功能 先创建两个组件

ng g component home

ng g component product

修改 home.component.html

<p>
  这里是主页组件
</p>

product.component.html

<p>
  这是是商品信息组件
</p>

修改 app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/product']">商品详情</a>

<router-outlet></router-outlet>

运行查看效果

选择商品的时候上面地址会自动变成http://localhost:4200/product

加个按钮  使用控制器跳转

<!--The content below is only a placeholder and can be replaced.-->
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/product']">商品详情</a>
<button type="button" value="商品详情" (click)="toProDuctDetails()" >商品详情</button>
<router-outlet></router-outlet>

修改 app.component.ts

import {Component} from '@angular/core';
import {Router} from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'router';

  constructor(private router: Router) {
  }

  toProDuctDetails() {
    this.router.navigate(['/product']);
  }
}

效果同routerLink

当用户使用不存在的路径

创建一个404组件

ng g component code404

修改 code404.component.html

<p>
  页面不存在
</p>

修改 app-routing.module.ts

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';

const routes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'product', component: ProductComponent},
  {path: '**', component: Code404Component},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

访问错了 http://localhost:4200/produ1

也能出现错误界面

在路由时传递数据

在查询参数时传递数据

/product?id=1&name=2   >>  ActivatedRoute.queryParams[id]

在路由的路径中传递数据

{path:/product/:id} >>  /product/1  ActivatedRoute.params[id]

在路由配置中传递数据

 {path:/product,comonent:ProductCompomemt,data[{isProd:true}]} >> ActivatedRoute.data[0][isProd]

下面我们继续修改demo 查看效果

 app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/product']" [queryParams]="{id:1}">商品详情</a>
<button type="button" value="商品详情" (click)="toProDuctDetails()" >商品详情</button>
<router-outlet></router-outlet>

product.component.ts

import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html',
  styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
  public productId: number;

  constructor(private routeInfo: ActivatedRoute) {
  }

  ngOnInit() {
    this.productId = this.routeInfo.snapshot.queryParams['id'];
  }

}
<p>
  这是是商品信息组件
</p>
<p>商品Id是{{productId}}</p>

下面第二种方式

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/product',1]" >商品详情</a>
<button type="button" value="商品详情" (click)="toProDuctDetails()" >商品详情</button>
<router-outlet></router-outlet>

app-routing.module.ts

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';

const routes: Routes = [
  {path: '', component: HomeComponent},
  {path: 'product/:id', component: ProductComponent},
  {path: '**', component: Code404Component},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

product.component.ts

import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html',
  styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
  public productId: number;

  constructor(private routeInfo: ActivatedRoute) {
  }

  ngOnInit() {
    this.productId = this.routeInfo.snapshot.params['id'];
  }

}

在写第三种方式的时候  我们要明确两个概念  参数快照 和参数订阅

修改 app.component.ts

import {Component} from '@angular/core';
import {Router} from '@angular/router';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'router';

  constructor(private router: Router) {
  }

  toProDuctDetails() {
    this.router.navigate(['/product', 2]);
  }
}

然后操作 网页

上面 主页  商品详情(p)  商品详情(button)  我们 称为1 2 3

点击 1  点击 3  效果正常 

点击 1 点击2  效果正常

点击 1 点击2 点击3 效果如上图所示

这是因为product.component.ts中的

  ngOnInit() {
    this.productId = this.routeInfo.snapshot.params['id'];
  }

使用了参数快照  

我们改成参数订阅试一下

import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html',
  styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
  public productId: number;

  constructor(private routeInfo: ActivatedRoute) {
  }

  ngOnInit() {
    this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']);
  }

}

 我们会发现 即使 点击 1 点击2 点击3 就可以了 效果就没问题了

重定向路由

在用户访问一个特定的地址时,将其重定向到另一个指定的地址、

www.aaa.com  >> www.aaa.com/products
www.aaa.com/x >> www.aaa.com/y

修改路由配置

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {path: 'home', component: HomeComponent},
  {path: 'product/:id', component: ProductComponent},
  {path: '**', component: Code404Component},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

子路由

{path:'home',component:HomeComponent}

 -----------------------------------------------------------------

{path:'home',component:HomeComponent,
 children:[

{path:'',component:XxxComponent,},
{path:'/yyy' compenent:YyyComponent}
]
}

 当访问home 路径的时候 还是会展现Home组件的模版 但是 

router-outlet会显示Xxx组价模版

同理 访问home/yyy的时候 还是会展现Home组件的模版 但是 

router-outlet会显示Yyy组价模版

现在我们来一步一步来实现子路由

新建两个组件

ng g component productDesc

ng g component sellerInfo

app-routing.module.ts

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
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},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

seller-info.component.ts

import {Component, OnInit} from '@angular/core';
import {ActivatedRoute} from '@angular/router';

@Component({
  selector: 'app-seller-info',
  templateUrl: './seller-info.component.html',
  styleUrls: ['./seller-info.component.css']
})
export class SellerInfoComponent implements OnInit {
  public sellerId: number;

  constructor(private routeInfo: ActivatedRoute) {
  }

  ngOnInit() {
    this.sellerId = this.routeInfo.snapshot.params['id'];
  }

}

seller-info.component.html

<p>
  销售员ID是{{sellerId}}
</p>

product-desc.component.html

<p>
  这是一个牛叉的商品
</p>

product.component.html

<p>
  这是是商品信息组件
</p>
<p>商品Id是{{productId}}</p>
<a href="#" [routerLink]="['./']">商品描述</a>
<a href="#" [routerLink]="['./seller',99]">销售员信息</a>
<router-outlet></router-outlet>

 子路由不仅仅可以嵌套两层 随着需求的复杂 可以无限往下嵌套

辅助路由

配置分为三步
<router-outlet></router-outlet>
<router-outlet name="aux"></router-outlet>

{path: 'xxx',component: XxxComponent,outlet: 'aux'}
{path: 'yyy',component: YxxComponent,outlet: 'aux'}


<a[routerLink]="['/home',{outles:{aux: 'xxx'}}]">Xxx</a>
<a[routerLink]="['/product',{outles:{aux: 'yyy'}}]">Yyy</a>

辅助路由案例 整体思路

在app 组件的模板上 再定义一个插座来显示聊天面板

单独开发一个聊天的插件,只显示在新定义的插座上

通过路由参数控制新插座是否显示聊天面板

ng g component chat

chat.component.html

<textarea placeholder="请输入聊天内容" class="chat"></textarea>
.chat{
  background: aquamarine;
  height: 100px;
  width: 30%;
  float: left;
  box-sizing: border-box;
}

home.component.html

.home{
  background: red;
  height: 100px;
  width: 70%;
  float: left;
  box-sizing: border-box;
}

product.component.html

<div class="product">
  <p>
    这是是商品信息组件
  </p>
  <p>商品Id是{{productId}}</p>
  <a href="#" [routerLink]="['./']">商品描述</a>
  <a href="#" [routerLink]="['./seller',99]">销售员信息</a>
  <router-outlet></router-outlet>

</div>

product.component.css

.product{
  background: yellow;
  height: 100px;
  width: 70%;
  float: left;
  box-sizing: border-box;
}

app-routing.module.ts

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {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},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule {
}

app.component.html

<!--The content below is only a placeholder and can be replaced.-->
<a [routerLink]="['/']">主页</a>
<a [routerLink]="['/product',1]">商品详情</a>
<button type="button" value="商品详情" (click)="toProDuctDetails()">商品详情</button>

<a [routerLink]="[{outlets:{ primary: 'home',aux:'chat'}}]">开始聊天</a>
<a [routerLink]="[{outlets:{aux:null}}]">结束聊天</a>

<p>
  <router-outlet></router-outlet>
  <router-outlet name="aux"></router-outlet>
</p>

其中 primary:'home'  是为了 不论每次切换到chat组件时候 主页都显示home 

路由守卫

 使用场景

  • 只有当用户已经登录并拥有某些权限时才能进入某些路由。
  • 一个由多个表单组件组成的向导,例如注册流程,用户只有在当前路由的组件中填写了满足需求的信息才可以导航到下一路由。
  • 当用户未执行保存操作而试图离开当前导航时提醒用户。

可以使用钩子来完成这些操作

有三种路由守卫

  • CanActivate:处理导航到某路由的情况。
  • CanDeactivate:处理当前路由离开的情况。
  • Resolve:在路由激活之前获取路由数据。

下面我们继续修改案例 只有当用户已经登录并拥有某些权限时才能进入产品路由

在app目录下 新建一个guard 文件夹 并新建一个 login.guard.ts

import {CanActivate} from '@angular/router';

export class LoginGuard implements CanActivate {
  canActivate() {
    const loggedIn: boolean = Math.random() < 0.5;
    if (!loggedIn) {
      console.log('用户未登录');
    }
    return loggedIn;
  }

}

修改路由app-routing.module.ts

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';
import {LoginGuard} from './guard/login.guard';
import {UnsavedGuard} from './guard/Unsaved.guard';

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {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],
  providers: [LoginGuard],
})
export class AppRoutingModule {
}

这样就只有百分之50的概率进入商品路由了 

我们再新建一个 CanDeactivate:处理当前路由离开的情况。

guard 文件下 新建 Unsaved.guard.ts

import {CanDeactivate} from '@angular/router';
import {ProductComponent} from '../product/product.component';

export class UnsavedGuard implements CanDeactivate<ProductComponent>{
  canDeactivate(component: ProductComponent) {
    return window.confirm('你还没有保存,确定要离开么?');
  }

}

修改app-routing.module.ts路由配置

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';
import {LoginGuard} from './guard/login.guard';
import {UnsavedGuard} from './guard/Unsaved.guard';

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {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],
  providers: [LoginGuard, UnsavedGuard],
})
export class AppRoutingModule {
}

当从商品离开到别的组件的时候 就会弹出对话框

下面使用Resolve守卫

在guard文件下 创建product.resolve.ts

代码如下

///<reference path="../../../node_modules/@angular/core/src/di/injectable.d.ts"/>
import {ActivatedRouteSnapshot, Resolve, Router, RouterStateSnapshot} from '@angular/router';
import {Product} from '../product/product.component';
import {Observable} from 'rxjs';
import {Injectable} from '@angular/core';

/**
 * // 这个类需要装饰器装饰一下

 */
@Injectable()
export class ProductResolve implements Resolve<Product> {
  constructor(private router: Router) {

  }

  resolve(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<Product> | Promise<Product> | Product {
    let productId: number = route.params['id'];
    if (productId == 1) {
      return new Product(1, 'iPhoneX');
    } else {
      this.router.navigate(['/home']);
      return undefined;
    }
  }

}

product.component.html

<div class="product">
  <p>
    这是是商品信息组件
  </p>
  <p>商品Id是{{productId}}</p>
  <p>商品名称是{{productName}}</p>
  <a href="#" [routerLink]="['./']">商品描述</a>
  <a href="#" [routerLink]="['./seller',99]">销售员信息</a>
  <router-outlet></router-outlet>

</div>

product.component.ts

import {Component, OnInit} from '@angular/core';
import {ActivatedRoute, Params} from '@angular/router';

@Component({
  selector: 'app-product',
  templateUrl: './product.component.html',
  styleUrls: ['./product.component.css']
})
export class ProductComponent implements OnInit {
  public productId: number;
  public productName: string;

  constructor(private routeInfo: ActivatedRoute) {
  }

  ngOnInit() {
    this.routeInfo.params.subscribe((params: Params) => this.productId = params['id']);
    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

import {NgModule} from '@angular/core';
import {Routes, RouterModule} from '@angular/router';
import {HomeComponent} from './home/home.component';
import {ProductComponent} from './product/product.component';
import {Code404Component} from './code404/code404.component';
import {ProductDescComponent} from './product-desc/product-desc.component';
import {SellerInfoComponent} from './seller-info/seller-info.component';
import {ChatComponent} from './chat/chat.component';
import {LoginGuard} from './guard/login.guard';
import {UnsavedGuard} from './guard/Unsaved.guard';
import {ProductResolve} from './guard/product.resolve';

const routes: Routes = [
  {path: '', redirectTo: '/home', pathMatch: 'full'},
  {path: 'chat', component: ChatComponent, outlet: 'aux'},
  {path: 'home', component: HomeComponent},
  {
    path: 'product/:id', component: ProductComponent,
    children: [
      {path: '', component: ProductDescComponent},
      {path: 'seller/:id', component: SellerInfoComponent},
    ], resolve: {product: ProductResolve}
  },
  {path: '**', component: Code404Component},
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule],
  providers: [LoginGuard, UnsavedGuard, ProductResolve],
})
export class AppRoutingModule {
}

我们之前命名的1 2 3 还记得么

点击1 的时候 主页 点击2 的时候 进的是id 1 所以能进去 显示苹果7 点击3 的时候 进去的是id 2 所以显示失败  进入了主页

下载项目

猜你喜欢

转载自blog.csdn.net/mp624183768/article/details/82855667