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,显然不太方便设置。所以还有另外一种风格,就是老式风格,和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 { }

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

  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

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);

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

  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

// 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)
  }
}

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

  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

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

时间: 2024-10-12 18:58:07

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

ios应用程序的两种启动方法

?1.概述 AFNetworking简称AFN,是iOS开发中干流第三方结构之一.苹果的一套结构NSContention发送央求与接纳央求的办法十分繁琐.操作起来很不便利.不只需做差异各种央求设置各种不相同的参数,而且还要经常在多线程里操作,一同还要对央求与回来的数据做各种序列化的操作,一同还要考虑央求数据的安全等成堆疑问.而AFNetworking帮我们简化了网络操作. 2.早前的几个网络结构 1.ASI结构: HTTP终结者.很牛, 但是有BUG, 现已间断更新. 2.MKNetworkKi

angular2系列教程(六)升级装备、pipe

今天,我们要讲的是angualr2的pipe这个知识点,但是在这之前我们需要升级一下我们的装备,因为之前的装备太“寒酸”了. 例子 这个例子包含两个pipe,一个是stateful,一个是stateless,是直接复制官方的例子.本例子还包含了我对AngularClass/angular2-webpack-starter这个牛逼starter的改写,我会详细讲解配置. 源代码 没有测试 AngularClass/angular2-webpack-starter 这里面包含了Angular 2 (

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

Unity3D脚本中文系列教程(十五) ◆ function OnPostprocessAudio (clip:AudioClip):void 描述:◆  function OnPostprocessGameObjectWithUserProperties (root : GameObject, propNames : string[], values : object[]) : void 描述:在导入文件时,为每个至少附加了一个用户属性的游戏物体调用propNames是一个string[ ],

基于Apache+Tomcat负载均衡的两种实现方法

Apache+Tomcat实现负载均衡的两种实现方法 如果我们将工作在不同平台的apache能够实现彼此间的高效通信,因此它需要一种底层机制来实现--叫做apr Apr的主要目的就是为了其能够让apache工作在不同的平台上,但在linux上安装apache的时候通常都是默认安装的 [[email protected] ~]#rpm -qi aprName                 :apr                                        Relocation

CRL快速开发框架系列教程十(导出对象结构)

本系列目录 CRL快速开发框架系列教程一(Code First数据表不需再关心) CRL快速开发框架系列教程二(基于Lambda表达式查询) CRL快速开发框架系列教程三(更新数据) CRL快速开发框架系列教程四(删除数据) CRL快速开发框架系列教程五(使用缓存) CRL快速开发框架系列教程六(分布式缓存解决方案) CRL快速开发框架系列教程七(使用事务) CRL快速开发框架系列教程八(使用CRL.Package) CRL快速开发框架系列教程九(导入/导出数据) CRL快速开发框架系列教程十(

angular2系列教程(一)hello world

今天我们要讲的是angular2系列教程的第一篇,主要是学习angular2的运行,以及感受conponents以及模板语法. http://lewis617.github.io/angular2-tutorial/hellowold/ 例子 这个例子非常简单,是个双向数据绑定. 运行方法: 全局安装http-server npm install -g http-server 公共部分 公共部分就是你直接复制粘贴拿去用的部分,包括: 1.index.html 2.lib 3.app/main.t

Android -- service两种启动方式startService与bindService

继上一篇文章,Android – Service的使用,我们来继续看看Service的两种启动方式 第一种startService . 运行代码,得知以下几点结论: 我们了解它的启动周期为onCreate->onStartCommand,如图 当退出应用后,后台的Service进程仍然存在,未被销毁 当点击多次startService时,如图我点击了3次,你会发现onCreate方法只创建了一次. stopService,如图 第二种bindService 运行代码得知以下结论: 点击bindS

visualvm远程监控jvm两种配置方法

参考:http://blog.itpub.net/17203031/viewspace-765810 一.Jstatd RMI远程监控方法 VisualVM在监控本地JVM的时候是很方便的.只要应用程序运行起来,我们就可以从VisualVM里面监控出来. 远程服务器上的JVM监控就需要一些额外的配置了.目前VisualVM支持两种监控方法:Jstatd方法和基于JMX的方法.我们先介绍Jstatd方法. Jstatd方法是利用后台的RMI守护进程来实现对远程JVM的监控. 1.查看一下JAVA_

C# 禁止windows程序重复运行的两种基本方法

一般的如果运行一个软件.让他处于运行状态,然后我们再去打开这个程序时就会提示我们“程序已启动或者不能重复启动此程序”,比如QQ对战平台 ,就限制一台机子启动两个QQ对战平台,那么他在C#中是如何实现的呢? 一般有两种方法,我是用的是第一种 方法1: 在项目的第一个窗体的启动事件中 如form1_load() 中添加如下语句=================================这是什么分割线==================================== #region 判断系