今天我们要讲的是ng2的路由系统。

例子

例子是官网的例子,包含一个“危机中心”和“英雄列表”,都在一个app中,通过路由来控制切换视图。还包含了promise的用法,服务的用法等多个知识点。

源代码:

https://github.com/lewis617/angular2-tutorial/tree/gh-pages/router

运行方法:

在根目录下运行:

  1. http-server

引入库文件设置base href

路由并不在ng2中,需要我们额外引入,另外我们需要设置base href,这是个什么东西呢?相当于我们后续所有url的“前缀”,因为我们的app默认是基于"HTML 5 pushState" 风格的路由,所以我们需要加上base href,来保证当我们导航到深层次的url时候,资源可以被正确加载:

index.html

  1. <!-- Add the router library -->
  2. <script src="lib/router.dev.js"></script>
  1. <!-- Set the base href -->
  2. <script>document.write('<base href="' + document.location + '" />');</script>

两种启动方法

app/main.ts

  1. import {bootstrap} from 'angular2/platform/browser';
  2. import {ROUTER_PROVIDERS} from 'angular2/router';
  3.  
  4. import {AppComponent} from './app.component';
  5.  
  6. // Add these symbols to override the `LocationStrategy`
  7. //import {provide} from 'angular2/core';
  8. //import {LocationStrategy,
  9. // HashLocationStrategy} from 'angular2/router';
  10.  
  11. bootstrap(AppComponent, [ROUTER_PROVIDERS,
  12. //provide(LocationStrategy,
  13. // {useClass: HashLocationStrategy}) // .../#/crisis-center/
  14. ]);

这种启动方法采取默认的"HTML 5 pushState" 风格,没有#号,但是存在一个弊端。就是当我们在子路经刷新浏览器时候,会出现404的错误。解决办法可以将所有的路由都指向根目录,但是我们使用了http-server,显然不太方便设置。(可以通过设置base href为“/“来解决!)所以还有另外一种风格,就是老式风格,和ng1一样的,带有#的路由风格,它的启动方法是:

  1. import {bootstrap} from 'angular2/platform/browser';
  2. import {ROUTER_PROVIDERS} from 'angular2/router';
  3.  
  4. import {AppComponent} from './app.component';
  5.  
  6. // Add these symbols to override the `LocationStrategy`
  7. import {provide} from 'angular2/core';
  8. import {LocationStrategy,
  9. HashLocationStrategy} from 'angular2/router';
  10.  
  11. bootstrap(AppComponent, [ROUTER_PROVIDERS,
  12. provide(LocationStrategy,
  13. {useClass: HashLocationStrategy}) // .../#/crisis-center/
  14. ]);

如此一来,我们的app的路由就全部带上#了,当你刷新页面时候,也不会出现404的错误了,但是url的可读性没有"HTML 5 pushState" 风格好看。

ROUTER_DIRECTIVES、RouteConfig、routerLink、router-outlet

路由的编写很简单,我们只需要在我们的组件中进行配置就行了:

app/app.component.ts

  1. import {Component} from 'angular2/core';
  2. import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router';
  3.  
  4. import {CrisisCenterComponent} from './crisis-center/crisis-center.component';
  5. import {HeroListComponent} from './heroes/hero-list.component';
  6. import {HeroDetailComponent} from './heroes/hero-detail.component';
  7.  
  8. import {DialogService} from './dialog.service';
  9. import {HeroService} from './heroes/hero.service';
  10.  
  11. @Component({
  12. selector: 'my-app',
  13. template: `
  14. <h1 class="title">Component Router</h1>
  15. <nav>
  16. <a [routerLink]="['CrisisCenter']">Crisis Center</a>
  17. <a [routerLink]="['Heroes']">Heroes</a>
  18. </nav>
  19. <router-outlet></router-outlet>
  20. `,
  21. providers: [DialogService, HeroService],
  22. directives: [ROUTER_DIRECTIVES]
  23. })
  24. @RouteConfig([
  25.  
  26. { // Crisis Center child route
  27. path: '/crisis-center/...',
  28. name: 'CrisisCenter',
  29. component: CrisisCenterComponent,
  30. useAsDefault: true
  31. },
  32.  
  33. {path: '/heroes', name: 'Heroes', component: HeroListComponent},
  34. {path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent},
  35. {path: '/disaster', name: 'Asteroid', redirectTo: ['CrisisCenter', 'CrisisDetail', {id:3}]}
  36. ])
  37. export class AppComponent { }

上述代码我们干了几件事:

  1. 写了一个组件,包含一个h1,一个nav里面包含两个a,还有一个router-outlet组件
  2. 注入了两个服务,DialogService, HeroService(这一步不属于路由构建步骤)
  3. 注入了一个指令,ROUTER_DIRECTIVES
  4. 使用@RouteConfig,配置子路径和对应的子组件,当/crisis-center/时候,在router-outlet中显示CrisisCenterComponent组件,当/heroes时候,在router-outlet中显示HeroListComponent组件,以此类推
  5. 当导航到/disaster,重定向到'CrisisCenter'的'CrisisDetail'视图。'CrisisCenter', 'CrisisDetail'是父子视图关系,下面会讲到。
  6. 导出这个组件

好了我们的带有路由的组件编写好了,其实就是个可以切换视图的组件而已,就是这么简单。我们在浏览器中运行程序,点击nav中的heroes,就可以把子视图Heroes渲染出来了。

浏览器路径变为http://localhost:63342/angular2-tutorial/router/index.html/heroes,在原有的基础上加上了/heroes。

温习promise

当我们导航到heroes视图的时候,我们就进入了另一个子组件,这个组件需要一个heroes服务,里面用到了promise,我们在angular2系列教程(七)Injectable、Promise、Interface、使用服务讲过promise,然我们来温习promise:

app/heroes/hero.service.ts

  1. import {Injectable} from 'angular2/core';
  2.  
  3. export class Hero {
  4. constructor(public id: number, public name: string) { }
  5. }
  6.  
  7. @Injectable()
  8. export class HeroService {
  9. getHeroes() { return heroesPromise; }
  10.  
  11. getHero(id: number | string) {
  12. return heroesPromise
  13. .then(heroes => heroes.filter(h => h.id === +id)[0]);
  14. }
  15. }
  16.  
  17. var HEROES = [
  18. new Hero(11, 'Mr. Nice'),
  19. new Hero(12, 'Narco'),
  20. new Hero(13, 'Bombasto'),
  21. new Hero(14, 'Celeritas'),
  22. new Hero(15, 'Magneta'),
  23. new Hero(16, 'RubberMan')
  24. ];
  25.  
  26. var heroesPromise = Promise.resolve(HEROES);

以上代码我们干了几件事:

  1. 写了一个Hero类
  2. 写了一个HeroService类,包含两个成员函数

  3. 写了一个数组HEROES,里面每一项都是一个hero类的实例,也就是个对象(引用类型)
  4. 定义了一个heroesPromise,将value设为数组HEROES,状态为resolved,随时可以使用then来获取value,也就是数组HEROES

温习promise,promise的两种构建方法:

  1. Promise.resolve()
  2. new Promise(),里面是个function,该function的参数是resolve和reject。

例子(chrome console):

更详细的的用法,可以看我之前讲的promise:angular2系列教程(七)Injectable、Promise、Interface、使用服务

两个服务:Router、RouteParams

我们的英雄服务写好了,然我们继续看英雄列表组件,当我们想要点击列表的某一项的时候,我们需要一个参数来导航到指定的英雄详情视图,这时候我们就需要RouteParams了,导航这个动作出发则需要Router服务:

app/heroes/hero-list.component.ts

  1. // TODO SOMEDAY: Feature Componetized like CrisisCenter
  2. import {Component, OnInit} from 'angular2/core';
  3. import {Hero, HeroService} from './hero.service';
  4. import {Router, RouteParams} from 'angular2/router';
  5.  
  6. @Component({
  7. template: `
  8. <h2>HEROES</h2>
  9. <ul class="items">
  10. <li *ngFor="#hero of heroes"
  11. [class.selected]="isSelected(hero)"
  12. (click)="onSelect(hero)">
  13. <span class="badge">{{hero.id}}</span> {{hero.name}}
  14. </li>
  15. </ul>
  16. `
  17. })
  18. export class HeroListComponent implements OnInit {
  19. heroes: Hero[];
  20.  
  21. private _selectedId: number;
  22.  
  23. constructor(
  24. private _service: HeroService,
  25. private _router: Router,
  26. routeParams: RouteParams) {
  27. this._selectedId = +routeParams.get('id');
  28. }
  29.  
  30. isSelected(hero: Hero) { return hero.id === this._selectedId; }
  31.  
  32. onSelect(hero: Hero) {
  33. this._router.navigate( ['HeroDetail', { id: hero.id }] );
  34. }
  35.  
  36. ngOnInit() {
  37. this._service.getHeroes().then(heroes => this.heroes = heroes)
  38. }
  39. }

以上代码,我们干了几件事:

  1. 渲染一个组件,包括一个列表
  2. 在构造函数中,将英雄服务HeroService、路由服务Router、路由参数RouteParams传给私有变量
  3. 写了三个成员函数用于处理相应的业务逻辑
  4. 其中this._router.navigate( ['HeroDetail', { id: hero.id }] );将app导航到了HeroDetail视图,并带上id参数
  5. 其中this._service.getHeroes().then(heroes => this.heroes = heroes),用于获取刚才的heroes数组,并将其传给this.heroes

Router服务的使用:this._router.navigate( ['HeroDetail', { id: hero.id }] );

RouteParams服务的使用:this._selectedId = +routeParams.get('id');   其中routeParams.get('id')前面那个+代表将其转换为数字类型。

HeroService服务的使用: this._service.getHeroes().then(heroes => this.heroes = heroes)

引用类型和单例模式的妙用

我们继续看英雄详细视图:

app/heroes/hero-detail.component.ts

  1. import {Component, OnInit} from 'angular2/core';
  2. import {Hero, HeroService} from './hero.service';
  3. import {RouteParams, Router} from 'angular2/router';
  4.  
  5. @Component({
  6. template: `
  7. <h2>HEROES</h2>
  8. <div *ngIf="hero">
  9. <h3>"{{hero.name}}"</h3>
  10. <div>
  11. <label>Id: </label>{{hero.id}}</div>
  12. <div>
  13. <label>Name: </label>
  14. <input [(ngModel)]="hero.name" placeholder="name"/>
  15. </div>
  16. <p>
  17. <button (click)="gotoHeroes()">Back</button>
  18. </p>
  19. </div>
  20. `,
  21. })
  22. export class HeroDetailComponent implements OnInit {
  23. hero: Hero;
  24.  
  25. constructor(
  26. private _router:Router,
  27. private _routeParams:RouteParams,
  28. private _service:HeroService){}
  29.  
  30. ngOnInit() {
  31. let id = this._routeParams.get('id');
  32. this._service.getHero(id).then(hero => this.hero = hero);
  33. }
  34.  
  35. gotoHeroes() {
  36. let heroId = this.hero ? this.hero.id : null;
  37. // Pass along the hero id if available
  38. // so that the HeroList component can select that hero.
  39. // Add a totally useless `foo` parameter for kicks.
  40. this._router.navigate(['Heroes', {id: heroId, foo: 'foo'} ]);
  41. }
  42. }

上述代码,我们仅仅是获取指定的英雄信息,并渲染出来。那么修改英雄信息是如何实现的呢?就是通过引用类型实现的。

我们知道,在js中,对象和数组是引用类型,也就意味着,当我们将某个对象传给别的变量的时候,仅仅是将对象的地址传给了那个变量,当我们修改那个变量时候,其实对象也被修改了。
在这个程序中,我们将hero对象传给this.hero,并将其双向数据绑定到input上:<input [(ngModel)]="hero.name" placeholder="name"/>,这样当我们改变input的值的时候,this.hero被改变,服务中的hero也被改变,因为是引用类型嘛,其实操作的都是一个对象。再有我们的服务是单例模式,所以全局的hero列表都被改变了。

让我们改变input的值,并点击back,我们发现英雄列表视图中的数据也被改变了,这就是引用类型和单例模式的妙用。

Route Parameters or Query Parameters?

当我们点击back返回时候,我们发现url变成了:http://localhost:63342/angular2-tutorial/router/index.html/heroes?id=11&foo=foo。也就是拥有了Query Parameters:?id=11&foo=foo。

为何会这样呢?因为我们指定了参数:this._router.navigate(['Heroes', {id: heroId, foo: 'foo'} ]);但是英雄列表视图有没有指定的id和foo的token,所以这两个变量是可选的,所以就自动生成了Query Parameters,好让我们进行select的css重绘。

在英雄详细视图中,我们使用了:id这个token。{path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}

这就是Route Parameters。它是必须的,我们必须要指定id这个参数。这就是Route Parameters 和 Query Parameters的比较。

这节课我们先讲到这里,下节课我们通过“危机中心”这个例子,继续讲解路由,将包含路由的嵌套、路由的生命周期等众多炫酷功能!


教程源代码及目录

如果您觉得本博客教程帮到了您,就赏颗星吧!

https://github.com/lewis617/angular2-tutorial

angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用的更多相关文章

  1. Service的两种启动方法

    刚才看到一个ppt,介绍service的两种启动方法以及两者之间的区别. startService 和 bindService startService被形容为我行我素,而bindService被形容 ...

  2. angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable

    大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...

  3. Android(java)学习笔记227:服务(service)之服务的生命周期 与 两种启动服务的区别

    1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.Service 的两种启动方法和区别: (1)Servi ...

  4. Android(java)学习笔记170:服务(service)之服务的生命周期 与 两种启动服务的区别

    1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.Service 的两种启动方法和区别: (1)Servi ...

  5. IOS-43-导航栏标题navigationItem.title不能改变颜色的两种解决方法

    IOS-43-导航栏标题navigationItem.title不能改变颜色的两种解决方法 IOS-43-导航栏标题navigationItem.title不能改变颜色的两种解决方法 两种方法只是形式 ...

  6. webpack4 系列教程(十二):处理第三方JavaScript库

    教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十二):处理第三方 JavaScript 库>原文地址.或者来我的小站看更多内容:godbm ...

  7. angular2系列教程(一)hello world

    今天我们要讲的是angular2系列教程的第一篇,主要是学习angular2的运行,以及感受angular2的components以及模板语法. 例子 这个例子非常简单,是个双向数据绑定.我使用了官网 ...

  8. Unity3D脚本中文系列教程(十六)

    Unity3D脚本中文系列教程(十五) ◆ function OnPostprocessAudio (clip:AudioClip):void 描述:◆  function OnPostprocess ...

  9. Unity3D脚本中文系列教程(十五)

    http://dong2008hong.blog.163.com/blog/static/4696882720140322449780/ Unity3D脚本中文系列教程(十四) ◆ LightRend ...

随机推荐

  1. MVVM框架从WPF移植到UWP遇到的问题和解决方法

    MVVM框架从WPF移植到UWP遇到的问题和解决方法 0x00 起因 这几天开始学习UWP了,之前有WPF经验,所以总体感觉还可以,看了一些基础概念和主题,写了几个测试程序,突然想起来了前一段时间在W ...

  2. C语言 · 字符转对比

    问题描述 给定两个仅由大写字母或小写字母组成的字符串(长度介于1到10之间),它们之间的关系是以下4中情况之一: 1:两个字符串长度不等.比如 Beijing 和 Hebei 2:两个字符串不仅长度相 ...

  3. ABP框架 - OData 集成

    文档目录 本节内容: 简介 安装 安装Nuget包 设置模块依赖 配置你的实体 创建控制器 示例 获取实体列表 请求 响应 获取单个实体 请求 响应 获取单个实体及导航属性 请求 响应 查询 请求 响 ...

  4. 网站定位之---根据IP获得区域

    记得以前做一个培训机构网站时候需要定位,那时候用的搜狐的api,不是很精准. demo:https://github.com/dunitian/LoTCodeBase/tree/master/NetC ...

  5. Canvas坐标系转换

    默认坐标系与当前坐标系 canvas中的坐标是从左上角开始的,x轴沿着水平方向(按像素)向右延伸,y轴沿垂直方向向下延伸.左上角坐标为x=0,y=0的点称作原点.在默认坐标系中,每一个点的坐标都是直接 ...

  6. CodeSimth - .Net Framework Data Provider 可能没有安装。解决方法

    今天想使用CodeSimth生成一个sqlite数据库的模板.当添加添加数据库的时候发现: .Net Framework Data Provider 可能没有安装. 下面找到官方的文档说明: SQLi ...

  7. 《动手实现一个网页加载进度loading》

    loading随处可见,比如一个app经常会有下拉刷新,上拉加载的功能,在刷新和加载的过程中为了让用户感知到 load 的过程,我们会使用一些过渡动画来表达.最常见的比如"转圈圈" ...

  8. A*算法应用[转]

    转自:http://www.cnblogs.com/zhoug2020/p/3468167.html 这是一篇十分精彩/易懂的博客,感谢原博主!本文通过自己的理解在原博文基础上突出一些重点字眼,句子. ...

  9. git添加GitHub远程库

    已经在本地创建了一个Git仓库后,又想在GitHub创建一个Git仓库,并且让这两个仓库进行远程同步,这样,GitHub上的仓库既可以作为备份,又可以让其他人通过该仓库来协作 首先,登陆GitHub, ...

  10. Ajax.BeginForm方法 参数

    感谢博主 http://www.cnblogs.com/zzgblog/p/5454019.html toyoung 在Asp.Net的MVC中的语法,在Razor页面中使用,替代JQuery的Aja ...