angular4学习指南(二)路由

news/2025/3/15 5:08:10/
路由
1.创建一个带路由模块的项目:ng new project --routing
2.基本路由之头部导航栏的实现
实现的效果如下:


点击“首页”,页面跳转到首页
点击“新闻”,页面跳转到新闻页面。
要实现如上效果,我们需要使用到angular中的路由概念,接下来我们一步步的实现上面的效果:
  • 创建两个组件 home和news : ng g component home 和ng g component news
  • 配置app-routing.module.ts文件中路由路径

const  routes: Routes = [
  {
    path'home',
    component: HomeComponent
  },
  {
    path'news',
    component: NewsComponent
  },
  {
    path'news/:nid'
component: NewsComponent
  },
  {
    /* 通配符 : 默认显示首页 */
    path'**',
    component: HomeComponent
  }
];

  • 在模板中实现导航栏的html代码,并添加路由连接的入口routerLink
  • 添加插座占位符<router-outlet></router-outlet>

< div  class= "wrapper" >
  < routerLink= "/home"  routerLinkActive= "active"  class= "nav-title home" > 首页 </ a >
  < routerLink= "/news/123"  routerLinkActive= "active"  class= "nav-title news" > 新闻 </ a >
</ div >
<router-outlet></router-outlet>

3.js中的路由跳转
在上面的实现中,我们实现了navbar的跳转连接,即这个入口是在页面上可以看到的,但是还有一种情况是我们请求了后台数据,会根据后台返回的数据来决定是不是要跳转路由,或者要跳转到哪个路由,比如我们点击了新闻列表页面的某条新闻,然后回去请求会新闻的详细信息,如果返回值不为空,才跳转到详情页,如果为空,可能需要跳转到error提示页面。那么在js中我们将用下面的方法跳转:
首先在component的构造函数constuctor中声明angular自带的Router模块,
然后在请求的数据的方法里使用this.route.navigate(['/path',param1,param2])来进行跳转,后两个表示参数。

constructor( private route: Router) { }

toNewsWithoutParam() {
alert( 'go to news!');
  this.route. navigate([ '/news']);
}
toNewsWithParam() {
  this.route. navigate([ '/news''123']);
}


4.在component中接收路由传递的参数
我们在上面的路由中传递了一个参数,所以在传递之后需要在component中接收路由的参数进行一些判断或者验证处理,接收参数需要进行下面两步:
  • 在构造函数中声明ActivedRoute的对象,
  • 调用this.activeRoute.params.subscribe((param) => param.username)

constructor( private routeActive: ActivatedRoute) { }

ngOnInit() {
  /*this.routeActive.params 是一个 Observable 的对象 */
  this.routeActive. params. subscribe((params) =>  this. nid = params.nid);
  console. log( this. nid);
}

5.Observable和观察者模式
观察者模式也叫发布订阅模式,举一个生活中例子:
有一个杂志出版社出一个《程序员指南》的杂志,每个月出版一本,年费198元,很多像我们一样机智聪明又漂酿的程序员付费并订阅了这个杂志,那每个月出版社负责出版并印刷杂志,到月末我们每个人可以收到一本当月的《程序员指南》,这个模式就是典型的观察者模式。
  • 期刊出版方 - 负责期刊的出版和发行工作
  • 订阅者 - 只需执行订阅操作,新版的期刊发布后,就会主动收到通知,如果取消订阅,以后就不会再收到通知

前端中经常中用到的观察者模式就是事件监听实现:

< div id= "btn"> 点击</div>
 
function clickHandler(){
console. log( " 点击了按钮 ");
}
document. getElementById( "btnn"). addEventListener( 'click', clickHandler);

//上面代码中我们用addEventListener API监听domd的click事件,一旦点击这个按钮就会触发clickHandle方法

RxJS 是基于观察者模式和迭代器模式以函数式编程思维来实现的。RxJS 中含有两个基本概念:Observables 与 Observer。Observables 作为被观察者,是一个值或事件的流集合;而 Observer 则作为观察者,根据 Observables 进行处理。

Observables 与 Observer 之间的订阅发布关系(观察者模式) 如下:

  • 订阅:Observer 通过 Observable 提供的 subscribe() 方法订阅 Observable。

  • 发布:Observable 通过回调 next 方法向 Observer 发布事件。
到这里就理解了 this.routeActive. params. subscribe((params) =>  this. nid  = params.nid);这句代码,我们订阅了activeRoute返回的这个流,并且将它的参数赋值给我们的nid变量
6.子路由
上面只是简单的实现了单层路由,假设我们有这样一个需求,如下图:

点击头部的导航后左边还有aside,点击不同的选项出来不同的内容,这时候就需要用到子路由。

下面我们来一步步的实现上述效果:
①首先新建两个父组件:ng g component home和ng g component news
②aside中的每个链接可以看做一个子组件,所以新建四个子组件
ng g component solution
ng g component about
ng g component sociaty
ng g component 
③按照上面的效果图实现两个组件的模板代码如下:
home.component.html
< div  class= "left left-aside" >
    < div  class= "navbar" >
      < ul >
        < li >
          < div >< routerLink= "/home/solution"  routerLinkActive= "aside-active"  class= "aside-title" > 解决方案 </ a ></ div >
        </ li >
        < li >
          < div >< routerLink= "/home/about"  routerLinkActive= "aside-active"  class= "aside-title" > 关于我们 </ a ></ div >
        </ li >
      </ ul >
    </ div >
</ div >
< div  class= "left content" >
    < router-outlet ></ router-outlet >
</div>
news.component.html

< div  class= "left left-aside" >
  < div  class= "navbar" >
    < ul >
      < li >
        < div >< routerLink= "/news/sociaty"  routerLinkActive= "aside-active"  class= "aside-title" > 社会新闻 </ a ></ div >
      </ li >
      < li >
        < div >< routerLink= "/news/hotnews"  routerLinkActive= "aside-active"  class= "aside-title" > 社会热点 </ a ></ div >
      </ li >
    </ ul >
  </ div >
</ div >
< div  class= "left content" >
  < router-outlet ></ router-outlet >
</div>

④在app-routing.module.ts中配置路由

{
  path'home',
  component: HomeComponent,
  children : [
    { path'solution'component: SolutionComponent},
    { path'about'component: AboutComponent},
    { path'**'component: SolutionComponent},
  ]
},
{
  path'news',
  component: NewsComponent,
  children : [
    { path'sociaty'component: SociatyComponent},
    { path'hotnews'component: HotnewsComponent},
    {path'**'component: SociatyComponent},
  ]
}

⑤在模板中添加跳转routerLink和插座<router-outlet></router-outlet>

到这里,上述的效果我们就已经实现了。

7.loadChildren-改进上述新闻的实现方式
在6中,我们实现了一开始说的效果,但是所有的路由都写在了父模块AppRoutingModule中,进一步分析当前的项目结构可以发现,home和news是上面两个navbar,它们每个都包含自己的子组件,那么,我们可以把home和news分别封装成子模块。以一个子模块home为例,接下来我们一步步去实现该效果:
①首先新建一个home的子模块,ng g module home
②定义子模块的内容,注意主路径为空,以及import中的forChild方法,在子 模块中声明所包含的子组件,要在主模块中删掉之前声明的组件,否则会报错。

const routes: Routes = [{path: '', //注意这里的主路径是空,因为在父模块中已经定义了前缀
    component: HomeComponent,children : [{path: 'solution', component: SolutionComponent},{path: 'about', component: AboutComponent},{path: '**', component: SolutionComponent},]}
];
@NgModule({imports: [CommonModule,RouterModule.forChild(routes)  //因为是子模块,所以在这里使用的是forChild方法
  ],declarations: [HomeComponent,  //子模块的组件必须在子模块中声明,否则会导致报错:Component HomeComponent is not part of any NgModule
    SolutionComponent, //同上
    AboutComponent, //同上
  ],exports: [RouterModule,HomeComponent]
})
export class HomeModule { }

③在主模块中使用loadChildren来加载子模块,注意在import中我们不需要引入子模块,这样就实现了懒加载的功能,只有当home路径被访问时才加载子模块HomeModule.

export const  routes: Routes = [
  {
    path'home',
    loadChildren : './home/home.module#HomeModule'
  },
  {
    path'news',
    component: NewsComponent,
    children : [
      { path'sociaty'component: SociatyComponent},
      { path'hotnews'component: HotnewsComponent},
      { path'**'component: SociatyComponent},
    ]
  },
  {
    path'news/:nid',
    component: NewsComponent
  },
  {
    /* 通配符 : 默认显示首页 */
    path'**',
    component: WelcomeComponent
  }
];

@NgModule({
  declarations: [
    AppComponent,
    NewsComponent,
    SociatyComponent,
    HotnewsComponent,
    WelcomeComponent,
    LoginComponent
  ],
  imports: [
    BrowserModule,
    RouterModule. forRoot( routes)
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

7.辅助路由
如果在一个页面要添加多个路由,这个时候就要用到辅助路由,我们以一个实例来一步步说明辅助路由的创建过程,实例的场景如下:我们在上面的新闻列表有一个聊天机器人的功能,当我们点击开始聊天时,聊天机器人窗口出现,结束时,机器人窗口隐藏。最终的效果图如下:



接下来我们一步步的实现该功能:
  • 创建聊天记录组件 ng g component chat
  • 定义辅助路由的输出插座位置

<!-- 这里显示 name aux 的辅助路由 -->
<router-outlet name="aux"></router-outlet>

  • 定义路由

{  path'chat',
component: ChatComponent,
  outlet'aux'   //输出到name=aux的插座上
}

  • 添加开始聊天和结束聊天的辅助路由入口

< [routerLink] =  "[{ outlets :{ primary :'home', aux :'chat'}}]" > 开始聊天 </ a
<[routerLink] = "[{outlets:{aux: null}}]">结束聊天</a>
  • 自定义chat组件的显示内容
这样,我们就创建一个辅助路由。

8.路由守卫
假设我现在有一个新的模块叫管理员模块,只有登录过的用户才可以访问,并且当进入到管理员模块后,如果点击了退出按钮,则需要弹出框询问一下用户是否需要确认离开该模块。路由守卫就是在进出路由时添加一些拦路虎(应该可以这么理解),在刚才描述的场景中,我们会用到路由守卫的两个概念:canActivate和canDeactive,路由守卫一共有下面几种,这里列出来:
  • CanActivate来处理导航进入某路由的情况。

  • CanActivateChild来处理导航进入某子路由的情况。
  • CanDeactivate来处理从当前路由离开的情况.

  • Resolve在路由激活之前获取路由数据。

  • CanLoad来处理 异步导航到某特性模块的情况。

接下来我们一步步的创建上述场景中的代码:
首先ng g component admin创建一个管理员模块

< div  class= "admin-wrappper" >
    < div >Hello,admin! </ div >
    < p > 这是管理员权限模块,只有管理员才可以看到呢~ </ p >
  < div >
    < button  (click)= " loginOut ()" > 退出 </ button >
  </ div >
</div>
然后在主模块中配置进入管理员模块的路由,进入时需要验证是否登录,所以需要canActivate守卫

{
  path'admin',
  component: AdminComponent,
  canActivate :[AuthGuardService]
  canDeactivate :[AuthGuardService]
}
如上面配置的,我们需要实现一个service去判断用户是否登录了,没登录就不能激活当前路由,那么 ng g service service/auth-guard生成一个路由守卫的服务
实现这个服务,这个类实现了canActivate的接口,并且需要重写这个方法(这个方法写判断登录的逻辑),如果没有登录路由就跳转到登录组件让用户去登录。

@Injectable()
export class AuthGuardService  implements CanActivate,CanDeactivate<AdminComponent> {
  constructor( private loginService:LoginService,  private dialogService:DialogService, private router:Router) { }
  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable< boolean> | Promise< boolean> |  boolean {
    console. log( "AuthGuard#canActived called...");
    const  urlstring = state. url;
    return this. checkLogin( url);
  }
  canDeactivate(component: AdminComponent, route: ActivatedRouteSnapshot, state: RouterStateSnapshot,
                nextState?: RouterStateSnapshot):  boolean | Observable< boolean> | Promise< boolean> {
    return this.dialogService. confirm( " 确定要退出管理员模块? ");
  }


  checkLogin(url: string) :  boolean {
    if( this.loginService. isLoginIn) {
      return true;
    }

    this.loginService. redirectURL = url;
    this.router. navigate([ '/login']);
    return false;
  }

}

由于需要一个登录判断服务,所以我们同样的新建一个登录服务和登录组件,和之前创建方法一样

实现loginService

@Injectable()
export class LoginService {
  constructor() { }
  isLoginIn false;
  redirectURL: string;

  login(): Observable< boolean> {
    return Observable. of( true).delay( 1000). do(val =>  this. isLoginIn true);
  }
  loginOut() {
    this. isLoginIn false;
  }

}

< div >
  < h3 > 我是登录子模块 </ h3 >
  < div >
    < span > 假设你已经输入了一大堆的表单信息 </ span >
    < div >< small > 因为我们还没有学到表单相关的信息 </ small ></ div >
  </ div >
  < div >
    < button  (click)= " toLogin ()" > 登录 </ button >
  </ div >
</div>

export class LoginComponent implements OnInit {constructor(private loginService:LoginService,private router:Router) { }message:string;ngOnInit() {this.setMessage();}setMessage() {this.message = "Logged"+ this.loginService.isLoginIn ? 'In':'Out';}toLogin() {this.message = "Try to login..."
     this.loginService.login().subscribe(()=> {this.setMessage();if(this.loginService.isLoginIn) {const redirectUrl = this.loginService.redirectURL ? this.loginService.redirectURL : '/admin';this.router.navigate([redirectUrl]);}});}


在主组件中添加管理员模块的入口,这时候点击,会先跳转到登录页面,点击登录后,才会进入管理员模块,当点击管理员模块的退出按钮时,会先弹出框确认你是否想真的离开?确定,退出管理员模块,跳转到home,否则继续留在管理员模块。

我们的案例中实现了两个路由守卫:canActivate和canDeactivate,剩下的自己可以去在这个基础上实现,宝宝写的太累咯~~

如果你感兴趣或者还是不明白,欢迎去下面的地址去download源码: https://github.com/Dan2Lin/angular-demo-route.git

PS:下一次我们会详细讲解angular表单模块的前世今生,同时也会给出实例和源码,欢迎继续追踪哦~


http://www.ppmy.cn/news/815722.html

相关文章

《计算机组成原理》第四章储存系统 部分课后习题答案 清华大学出版_秦磊华_谭志虎

4.1 存取时间:又称为存储器的访问时间,是指启动一次存储器的操作(读或写分别对应存与取)到该操作完成所经历的时间。 存取周期:连续启动两次访问操作之间的最短时间间隔。 存储器带宽:单位时间内存储器所能传输的信息量,常用的单位包括位/秒或字节/秒。 存储单元:保存…

ip6服务器位置,ipv6地址一般设置多少_ipv6地址设置指南

IPv6(Internet Protocol Version 6)是IETF(互联网工程任务组)设计的用于替代现行版本IP协议(IPv4)的下一代IP协议。目前IP协议的版本号是4(简称为IPv4),它的下一个版本就是IPv6。随着IPv4资源的急剧紧缺,相信在不久的未来,IPv6将成为最一代互联网地址的标准。与IPv4相比,IP…

《计算机组成原理》第五章指令系统 部分课后习题答案 清华大学出版_秦磊华_谭志虎

5.1 指令:控制计算机执行某种操作(如加、减、传送、转移等操作)的命令。 指令系统:一台计算机中所有指令的集合。 操作码:指令中用于控制指令操作性质的字段。 扩展地址码:将指令的操作码字段向不用的地址码字段扩展,从而在指令长度不变的情况下支持更多的指令。 地址…

计算机组成原理微课版(谭志虎主编)预习笔记

更新中 本专栏是博主在大学浪了近两年啥也没学后重学计算机专业时写的&#xff0c;方便个人学习和复习&#xff0c;本人菜鸟一枚&#xff0c;如有错误或能优化的地方欢迎指正&#xff0c;也特别欢迎交流学习。 motto共勉&#xff1a;心之所向&#xff0c;素履以往&#xff1b;…

python输出变量地址_Python——变量

笑虎:千行代码入门Python​zhuanlan.zhihu.com 函数的参数​www.liaoxuefeng.com Python学习之变量的作用域 - fireporsche - 博客园​www.cnblogs.com 1.Python数据类型: Python数据类型有两种:哈希类型、不可哈希类型,这是因为hash函数的结果和变量的地址有关。 哈希类型…

Python版春节快乐虎年大吉代码

春节即将到来之际&#xff0c;用Python写了春节快乐的祝福代码&#xff0c;程序运行加载的背景图各背景音乐均可自行选择&#xff0c;设置动态效果阶段模拟放烟花的过程。首先是粒子扩张阶段&#xff0c;再是停留阶段&#xff0c;然后是自由落体阶段&#xff0c;最后是消失。同…

win10系统ipv6服务器地址,win10系统设置ipV6地址的处理办法

win10系统使用久了&#xff0c;好多网友反馈说关于对win10系统设置ipV6地址设置的方法&#xff0c;在使用win10系统的过程中经常不知道如何去对win10系统设置ipV6地址进行设置&#xff0c;有什么好的办法去设置win10系统设置ipV6地址呢&#xff1f;在这里小编教你只需要1、安装…

单片机两只老虎c语言,蜂鸣器 唱 两只老虎 单片机程序

#include//包含52单片机寄存器定义的头文件 sbit soundP3^7; //将sound位定义为P3.7 unsigned int C; //储存定时器的定时常数 //以下是C调中音的音频宏定义 #define dao 523 //将"dao"宏定义为中音"1"的频率523Hz #define re 587 //将"re"宏定义…