七周七种前端框架三: Angular2 印象

巨大变化

在 Angular1 中最重要的两个概念:

  1. directive 拓展DOM功能,封装DOM操作,并且是可复用的组件
  2. controller 创建新的 $scope 作用域,封装和DOM无关的业务逻辑

Angular2 是一次彻底的重写,完全删掉了 Controller 和 $scope,增加了一个重要的功能叫 @Component,Component 是Angular2的核心,相当于Angular1 中 Directive 和 Controller 的合体。
并且 directive 功能简化, 和 Angular1 中的 Directive 完全不可以相提并论。

Angular2 用 TypeScript 写的,TypeScript 可以简单理解为 ES6 + Annotation + 强类型。

不过注意截止到今天(2015-11-20) angular2依然是Alpha 版本,也就是说API 还不稳定,还有很多bug,一些功能也没有完全实现。所以Beta版本出来之前不建议生产环境使用。

Angular 2 的一个Component 是这样的:

import {Component, bootstrap} from 'angular2/angular2';
@Component({
  selector: 'my-app',
  template: `
    <h1>{{title}}</h1>
    <h2>My favorite hero is: {{myHero}}</h2>
    `
})
export class AppComponent {
  title = 'Tour of Heroes';
  myHero = 'Windstorm';
}
bootstrap(AppComponent);

其中 @Component 是一个annotation,Java中有一个类似的翻译为注解。

上面是一个基本的Angular2 Component。分为这几部分:

  1. import 需要使用的模块
  2. 创建了一个Component,一个Component由注解和类两部分组成。@Component 是对 AppComponent 的一个注解,可以理解为对AppComponent的一个配置,声明了这个类是一个Component,并且对他做了一些配置。
  3. 引导这个Component。其实就是在html中找到对应的选择器并创建和绑定一个实力。

Component

Angular2 中把一块逻辑封装成一个 Component 类。 Component即负责渲染模板和监听事件,也封装业务逻辑。

一个 Component 由两部分组成:

  1. @Component 注解,这里声明了很多配置项,比如: selector, template, directives, styles, pipes 等。
  2. 一个普通的类,这个类封装了Component的所有业务逻辑,比如事件回调,数据处理等。

前面有写过ES6的博客,Annotation并不是ES6规范的一部分,但是ES7中的 decorator 其实和 Annotation应该是同一个东西,都是decorator模式。关于ES7的decorator参见这里 https://github.com/wycats/javascript-decorators/blob/master/README.md。

虽然 directive 和 Angular1 中的有很大不同,但是基本的一些内置directive除了写法变了基本还是大同小异的,在模板中的用法也基本不变,比如这个循环:

 <ul>
  <li *ng-for="#hero of heroes">
    {{ hero }}
  </li>
</ul>

不过Angular2严格的模块化组织代码,所以用到任何功能都需要先 import:

 import {Component, bootstrap, NgFor} from 'angular2/angular2';

并且然后还需要在 注解中声明才可以用。

Angular2 提供了 常见的 NgFor, NgIf 等流程控制directive。双向绑定的directive 还是叫 NgModel ,它属于Form的一部分,Angular2还是专门对form做了很多工作。

DI 依赖注入

依赖注入分两部分,一个是提供,一个是使用。
在Angular2 中通过 @Injectable 来声明一个可以被注入的模块:

@Injectable()
class HeroService {
  heroes: Hero[];
  constructor(private logger: Logger) {
    this.heroes = HEROES;
  }
  getHeroes() {
    this.logger.log('Getting heroes ...')
    return this.heroes;
  }
}

这样我们就提供了一个 可以被注入的模块

使用的时候 会稍微麻烦一些,使用的时候分两步。第一步是在自己的Component的构造函数中声明要注入的模块:

constructor(heroService: HeroService) {
  this.heroes = heroService.getHeroes();
}

这里根据参数的类型申明就知道需要注入的模块叫 HeroService

第二步比较奇怪一点,就是要声明一个provider。也就是说上面的代码中的 HeroSerivce 我们需要声明是谁提供了它。可能会有人有疑问了,我们不是最开始的时候定义了一个 HeroService 了嘛,为毛还要再声明一次。

这其实是为了灵活性考虑,我们提供了一个 HeroService ,然后在 Component 中又声明了需要注入一个叫 HeroService 的模块。但是注意,其实HeroService 只是一种实现而已,我们完全可以有多种方式来实现 HeroServive。比如我们测试的时候可能希望注入一个 MockHeroService,总不能在测试的时候改掉我们 Component的构造函数吧,最方便的就是我们在测试的时候申明 MockHeroService 就是Component 需要的 HeroService。

基于这个原因,我们需要声明一个 provider 来提供 HeroService 服务。这个声明是放在这里的:

bootstrap(AppComponent, [HeroService]);

奇怪了,不是没有什么provider吗。其实这是一个简写,[HeroService] 等价于 [provide(HeroService, {useClass:HeroService})]; 也就是默认用同名的类来提供。

注意 useClass,表示从这个类创建一个新的实例,不过这个实例是单例的。 也可以不用创建,直接给一个值,这个时候要用 useValue.

Pipe

Pipe 是一个数据处理管道。主要作用就是对数据输出的时候进行一些格式化处理。一个基本的date 管道的用法如下:

 <p>The hero's birthday is {{ birthday | date:"MM/dd/yy" }} /p>

其中 竖线是调用管道的语法,date是管道名字,后面的字符串是传入的参数。因为是管道,所以可以把多个管道连接起来:

 The chained hero's birthday is  {{ birthday | date | uppercase}}

我们可以自定义pipe。一个自定义的pipe的作用就是接受一个输入值和参数,然后返回一个处理后的值。

import {Pipe} from 'angular2/angular2';
/*
 * Raise the value exponentially
 * Takes an exponent argument that defaults to 1.
 * Usage:
 *   value | exponentialStrength:exponent
 * Example:
 *   {{ 2 |  exponentialStrength:10}}
 *   formats to: 1024
*/
@Pipe({
  name: 'exponentialStrength'
})
export class ExponentialStrengthPipe {
  transform(value:number, args:string[]) : any {
    return Math.pow(value, parseInt(args[0] || '1', 10));
  }
}

这里我们同样会用到annotation来声明一个管道。

代码

贴一段代码,这是官方的教程的,不过官方教程没写完,我加入了一个依赖注入的代码:

app.ts:

import {bootstrap, Component, FORM_DIRECTIVES, CORE_DIRECTIVES} from 'angular2/angular2';
import {HeroService, Hero} from "./heros";

@Component({
  selector: 'my-app',
  template: `
  <h1>My Heros</h1>
  <ul>
    <li *ng-for="#hero of heroes" (click)="onSelect(hero)" [ng-class]="getSelectedClass(hero)">{{hero.id}}:{{hero.name}}</li>
  </ul>
  <div *ng-if="selectedHero" >
    <h2>{{selectedHero.name}} details!</h2>
      <div>
        id: {{selectedHero.id}}
      <div>
      <label>name: </label>
      <input [(ng-model)]="selectedHero.name" placeholder="name" />
    </div>
  </div>
  `,
  styles: [`
    .selected {
      background: blue;
      color: white;
    }
  `],
  directives: [FORM_DIRECTIVES, CORE_DIRECTIVES]
})
class AppComponent {
  public title = 'Tour of Heroes';
  public heroes;

  public selectedHero: Hero;

  constructor(heroService: HeroService) {
    this.heroes = heroService.getHeroes();
  }

  public onSelect(hero: Hero) {
    this.selectedHero = hero;
  }

  public getSelectedClass(hero: Hero) {
    return { "selected" : hero == this.selectedHero }
  }
}

bootstrap(AppComponent, [HeroService]);

heros.ts:

import {Injectable} from 'angular2/angular2';

@Injectable()
export class HeroService {
  getHeroes() {

    var HEROES: Hero[] = [
      { "id": 11, "name": "Mr. Nice" },
      { "id": 12, "name": "Narco" },
      { "id": 13, "name": "Bombasto" },
      { "id": 14, "name": "Celeritas" },
      { "id": 15, "name": "Magneta" },
      { "id": 16, "name": "RubberMan" },
      { "id": 17, "name": "Dynama" },
      { "id": 18, "name": "Dr IQ" },
      { "id": 19, "name": "Magma" },
      { "id": 20, "name": "Tornado" }
    ];

    return HEROES;
  }
}

export class Hero {
  id: number;
  name: string;
}

参考:

  • https://dzone.com/articles/typed-front-end-with-angular-2

你可能感兴趣的:(angular2)