angular2系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用
今天我们要讲的是ng2的路由系统。
例子
例子是官网的例子,包含一个“危机中心”和“英雄列表”,都在一个app中,通过路由来控制切换视图。还包含了promise的用法,服务的用法等多个知识点。
源代码:
https://github.com/lewis617/angular2-tutorial/tree/gh-pages/router
运行方法:
在根目录下运行:
http-server
引入库文件设置base href
路由并不在ng2中,需要我们额外引入,另外我们需要设置base href,这是个什么东西呢?相当于我们后续所有url的“前缀”,因为我们的app默认是基于"HTML 5 pushState" 风格的路由,所以我们需要加上base href,来保证当我们导航到深层次的url时候,资源可以被正确加载:
index.html
<!-- Add the router library --> <script src="lib/router.dev.js"></script>
<!-- Set the base href --> <script>document.write('<base href="' + document.location + '" />');</script>
两种启动方法
app/main.ts
import {bootstrap} from 'angular2/platform/browser'; import {ROUTER_PROVIDERS} from 'angular2/router'; import {AppComponent} from './app.component'; // Add these symbols to override the `LocationStrategy` //import {provide} from 'angular2/core'; //import {LocationStrategy, // HashLocationStrategy} from 'angular2/router'; bootstrap(AppComponent, [ROUTER_PROVIDERS, //provide(LocationStrategy, // {useClass: HashLocationStrategy}) // .../#/crisis-center/ ]);
这种启动方法采取默认的"HTML 5 pushState" 风格,没有#号,但是存在一个弊端。就是当我们在子路经刷新浏览器时候,会出现404的错误。解决办法可以将所有的路由都指向根目录,但是我们使用了http-server,显然不太方便设置。(可以通过设置base href为“/“来解决!)所以还有另外一种风格,就是老式风格,和ng1一样的,带有#的路由风格,它的启动方法是:
import {bootstrap} from 'angular2/platform/browser'; import {ROUTER_PROVIDERS} from 'angular2/router'; import {AppComponent} from './app.component'; // Add these symbols to override the `LocationStrategy` import {provide} from 'angular2/core'; import {LocationStrategy, HashLocationStrategy} from 'angular2/router'; bootstrap(AppComponent, [ROUTER_PROVIDERS, provide(LocationStrategy, {useClass: HashLocationStrategy}) // .../#/crisis-center/ ]);
如此一来,我们的app的路由就全部带上#了,当你刷新页面时候,也不会出现404的错误了,但是url的可读性没有"HTML 5 pushState" 风格好看。
ROUTER_DIRECTIVES、RouteConfig、routerLink、router-outlet
路由的编写很简单,我们只需要在我们的组件中进行配置就行了:
app/app.component.ts
import {Component} from 'angular2/core'; import {RouteConfig, ROUTER_DIRECTIVES} from 'angular2/router'; import {CrisisCenterComponent} from './crisis-center/crisis-center.component'; import {HeroListComponent} from './heroes/hero-list.component'; import {HeroDetailComponent} from './heroes/hero-detail.component'; import {DialogService} from './dialog.service'; import {HeroService} from './heroes/hero.service'; @Component({ selector: 'my-app', template: ` <h1 class="title">Component Router</h1> <nav> <a [routerLink]="['CrisisCenter']">Crisis Center</a> <a [routerLink]="['Heroes']">Heroes</a> </nav> <router-outlet></router-outlet> `, providers: [DialogService, HeroService], directives: [ROUTER_DIRECTIVES] }) @RouteConfig([ { // Crisis Center child route path: '/crisis-center/...', name: 'CrisisCenter', component: CrisisCenterComponent, useAsDefault: true }, {path: '/heroes', name: 'Heroes', component: HeroListComponent}, {path: '/hero/:id', name: 'HeroDetail', component: HeroDetailComponent}, {path: '/disaster', name: 'Asteroid', redirectTo: ['CrisisCenter', 'CrisisDetail', {id:3}]} ]) export class AppComponent { }
上述代码我们干了几件事:
- 写了一个组件,包含一个h1,一个nav里面包含两个a,还有一个router-outlet组件
- 注入了两个服务,DialogService, HeroService(这一步不属于路由构建步骤)
- 注入了一个指令,ROUTER_DIRECTIVES
- 使用@RouteConfig,配置子路径和对应的子组件,当/crisis-center/时候,在router-outlet中显示CrisisCenterComponent组件,当/heroes时候,在router-outlet中显示HeroListComponent组件,以此类推
- 当导航到/disaster,重定向到'CrisisCenter'的'CrisisDetail'视图。'CrisisCenter', 'CrisisDetail'是父子视图关系,下面会讲到。
- 导出这个组件
好了我们的带有路由的组件编写好了,其实就是个可以切换视图的组件而已,就是这么简单。我们在浏览器中运行程序,点击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
import {Injectable} from 'angular2/core'; export class Hero { constructor(public id: number, public name: string) { } } @Injectable() export class HeroService { getHeroes() { return heroesPromise; } getHero(id: number | string) { return heroesPromise .then(heroes => heroes.filter(h => h.id === +id)[0]); } } var HEROES = [ new Hero(11, 'Mr. Nice'), new Hero(12, 'Narco'), new Hero(13, 'Bombasto'), new Hero(14, 'Celeritas'), new Hero(15, 'Magneta'), new Hero(16, 'RubberMan') ]; var heroesPromise = Promise.resolve(HEROES);
以上代码我们干了几件事:
- 写了一个Hero类
写了一个HeroService类,包含两个成员函数
- 写了一个数组HEROES,里面每一项都是一个hero类的实例,也就是个对象(引用类型)
- 定义了一个heroesPromise,将value设为数组HEROES,状态为resolved,随时可以使用then来获取value,也就是数组HEROES
温习promise,promise的两种构建方法:
- Promise.resolve()
- new Promise(),里面是个function,该function的参数是resolve和reject。
例子(chrome console):
更详细的的用法,可以看我之前讲的promise:angular2系列教程(七)Injectable、Promise、Interface、使用服务
两个服务:Router、RouteParams
我们的英雄服务写好了,然我们继续看英雄列表组件,当我们想要点击列表的某一项的时候,我们需要一个参数来导航到指定的英雄详情视图,这时候我们就需要RouteParams了,导航这个动作出发则需要Router服务:
app/heroes/hero-list.component.ts
// TODO SOMEDAY: Feature Componetized like CrisisCenter import {Component, OnInit} from 'angular2/core'; import {Hero, HeroService} from './hero.service'; import {Router, RouteParams} from 'angular2/router'; @Component({ template: ` <h2>HEROES</h2> <ul class="items"> <li *ngFor="#hero of heroes" [class.selected]="isSelected(hero)" (click)="onSelect(hero)"> <span class="badge">{{hero.id}}</span> {{hero.name}} </li> </ul> ` }) export class HeroListComponent implements OnInit { heroes: Hero[]; private _selectedId: number; constructor( private _service: HeroService, private _router: Router, routeParams: RouteParams) { this._selectedId = +routeParams.get('id'); } isSelected(hero: Hero) { return hero.id === this._selectedId; } onSelect(hero: Hero) { this._router.navigate( ['HeroDetail', { id: hero.id }] ); } ngOnInit() { this._service.getHeroes().then(heroes => this.heroes = heroes) } }
以上代码,我们干了几件事:
- 渲染一个组件,包括一个列表
- 在构造函数中,将英雄服务HeroService、路由服务Router、路由参数RouteParams传给私有变量
- 写了三个成员函数用于处理相应的业务逻辑
- 其中this._router.navigate( ['HeroDetail', { id: hero.id }] );将app导航到了HeroDetail视图,并带上id参数
- 其中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
import {Component, OnInit} from 'angular2/core'; import {Hero, HeroService} from './hero.service'; import {RouteParams, Router} from 'angular2/router'; @Component({ template: ` <h2>HEROES</h2> <div *ngIf="hero"> <h3>"{{hero.name}}"</h3> <div> <label>Id: </label>{{hero.id}}</div> <div> <label>Name: </label> <input [(ngModel)]="hero.name" placeholder="name"/> </div> <p> <button (click)="gotoHeroes()">Back</button> </p> </div> `, }) export class HeroDetailComponent implements OnInit { hero: Hero; constructor( private _router:Router, private _routeParams:RouteParams, private _service:HeroService){} ngOnInit() { let id = this._routeParams.get('id'); this._service.getHero(id).then(hero => this.hero = hero); } gotoHeroes() { let heroId = this.hero ? this.hero.id : null; // Pass along the hero id if available // so that the HeroList component can select that hero. // Add a totally useless `foo` parameter for kicks. this._router.navigate(['Heroes', {id: heroId, foo: 'foo'} ]); } }
上述代码,我们仅仅是获取指定的英雄信息,并渲染出来。那么修改英雄信息是如何实现的呢?就是通过引用类型实现的。
我们知道,在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系列教程(十)两种启动方法、两个路由服务、引用类型和单例模式的妙用的更多相关文章
- Service的两种启动方法
刚才看到一个ppt,介绍service的两种启动方法以及两者之间的区别. startService 和 bindService startService被形容为我行我素,而bindService被形容 ...
- angular2系列教程(八)In-memory web api、HTTP服务、依赖注入、Observable
大家好,今天我们要讲是angular2的http功能模块,这个功能模块的代码不在angular2里面,需要我们另外引入: index.html <script src="lib/htt ...
- Android(java)学习笔记227:服务(service)之服务的生命周期 与 两种启动服务的区别
1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.Service 的两种启动方法和区别: (1)Servi ...
- Android(java)学习笔记170:服务(service)之服务的生命周期 与 两种启动服务的区别
1.之前我们在Android(java)学习笔记171:Service生命周期 (2015-08-18 10:56)说明过,可以回头看看: 2.Service 的两种启动方法和区别: (1)Servi ...
- IOS-43-导航栏标题navigationItem.title不能改变颜色的两种解决方法
IOS-43-导航栏标题navigationItem.title不能改变颜色的两种解决方法 IOS-43-导航栏标题navigationItem.title不能改变颜色的两种解决方法 两种方法只是形式 ...
- webpack4 系列教程(十二):处理第三方JavaScript库
教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十二):处理第三方 JavaScript 库>原文地址.或者来我的小站看更多内容:godbm ...
- angular2系列教程(一)hello world
今天我们要讲的是angular2系列教程的第一篇,主要是学习angular2的运行,以及感受angular2的components以及模板语法. 例子 这个例子非常简单,是个双向数据绑定.我使用了官网 ...
- Unity3D脚本中文系列教程(十六)
Unity3D脚本中文系列教程(十五) ◆ function OnPostprocessAudio (clip:AudioClip):void 描述:◆ function OnPostprocess ...
- Unity3D脚本中文系列教程(十五)
http://dong2008hong.blog.163.com/blog/static/4696882720140322449780/ Unity3D脚本中文系列教程(十四) ◆ LightRend ...
随机推荐
- 从RPC开始(一)
这是一篇关于纯C++RPC框架的文章.所以,我们先看看,我们有什么? 1.一个什么都能干的C++.(前提是,你什么都干了) 2.原始的Socket接口,还是C API.还得自己去二次封装... 3.C ...
- 谈谈一些有趣的CSS题目(三)-- 层叠顺序与堆栈上下文知多少
开本系列,讨论一些有趣的 CSS 题目,抛开实用性而言,一些题目为了拓宽一下解决问题的思路,此外,涉及一些容易忽视的 CSS 细节. 解题不考虑兼容性,题目天马行空,想到什么说什么,如果解题中有你感觉 ...
- H5单页面手势滑屏切换原理
H5单页面手势滑屏切换是采用HTML5 触摸事件(Touch) 和 CSS3动画(Transform,Transition)来实现的,效果图如下所示,本文简单说一下其实现原理和主要思路. 1.实现原理 ...
- PHP 5.6 编译安装选项说明
`configure' configures this package to adapt to many kinds of systems. Usage: ./configure [OPTION].. ...
- 了解PHP中的Array数组和foreach
1. 了解数组 PHP 中的数组实际上是一个有序映射.映射是一种把 values 关联到 keys 的类型.详细的解释可参见:PHP.net中的Array数组 . 2.例子:一般的数组 这里,我 ...
- android通过webview调起支付宝app支付
webview在加载网页的时候会默认调起手机自带的浏览器加载网页,用户体验不好.但当用户设置浏览器客户端(setWebViewClient)设置这样的监听事件之后,当请求url的时候就不会打开手机自带 ...
- [C#] C# 知识回顾 - 异常介绍
异常介绍 我们平时在写程序时,无意中(或技术不够),而导致程序运行时出现意外(或异常),对于这个问题, C# 有专门的异常处理程序. 异常处理所涉及到的关键字有 try.catch 和 finally ...
- BPM费控管理解决方案分享
一.方案概述费用是除经营成本外企业的最主要支出,费用管理是财务管理的核心之一,加强企业内控管理如:费用申请.费用报销.费用分摊.费用审批.费用控制和费用支付等,通过科学有效的管理方法规范企业费用管理, ...
- 程序员装B指南
一.准备工作 "工欲善其事必先利其器." 1.电脑不一定要配置高,但是双屏是必须的,越大越好,能一个横屏一个竖屏更好.一个用来查资料,一个用来写代码.总之要显得信息量很大,效率很高 ...
- 技术笔记:Indy控件发送邮件
工作中有个需求需要发送邮件,因为使用的delphi6,所以自然就选择了indy组件,想想这事挺简单的.实现的过程倒是简单,看着Indy的demo很快就完了,毕竟也不是很复杂的功能. 功能要求: 1.压 ...