angular2初入眼帘之-了解component

前集回顾html

上一章里咱们讲了如何为angular2搭建开发环境(还没搭起来的赶忙去看哦),并使之跑起来咱们的第一个"My First Angular 2 App"。固然也有很多朋友反映环境搭建彷佛比较复杂,整整一篇教程,最后只简单输出了一句话!这里我要说一句,学习新知识的确有一个阵痛的过程,尤为像angular2这种框架,引入了大量之前"前端"并不关心(没有需求)的技术栈,这使得对于以前没有接触过这些概念的朋友的学习曲线陡然飙升,相信很多人看了上一章里开篇时的那些名词后已经认识到这一点了!本教程主打实际操做,但也不会彻底忽略理论,咱们边作边理解。今天就接着上一章的余温,咱们来写一个简单component前端

本章源码:componentwebpack

本章使用angular2版本为:2.4.5, webpack版本为: 2.2.0git

先来看看咱们将要完成的效果图:angularjs

图片描述

(注意动画的部分)很是简单的一个component,有木有?那好,咱们如今要作的就是为这样一个component描述需求:es6

  1. 她要能接受一个object用来描述初始值,如:isChecked(是否选中)、 txt(显示文本)github

  2. 当选中时,须要有横线覆盖文本;反之亦然web

  3. 当用户点击复选框时,须要向上广播该事件,由父组件(调用方)决定点击时该作什么。这里咱们须要在父组件里改变componentisChecked状态,并使component重绘typescript

  4. 她必须是一个处理Unidirectional Data Flow(单向数据流)的component,意思是传入参数必须不可变(Immutable)shell

注:第4步里,咱们使用Unidirectional Data Flow模型来更新数据,并无涉及到任何Reactive Programming的知识点

为了完成以上需求,咱们须要了解下面知识点

什么是component

或者这么问,AngularJS里有directiveangular2里有component,他们是什么关系?该如何理解angular2里的component?原谅我这里就再也不详述AngularJS里的directive了,直接介绍component

  1. Component: 简单说,就是带templatedirective,也是最多见的组件形式。譬如:上一章中,ts/app.ts里的AppComponent

  2. Structural directive: 经过增长/删除DOM元素改变DOM布局的directive。譬如:NgForNgIf

  3. Attribute directive: 控制DOM元素显示/隐藏,或者改变元素行为的directive。譬如:NgStyle

设计use case

看过我以前介绍以BDD手写依赖注入(dependency injection)的朋友应该已经对"行为驱动"多少有些了解了。当咱们须要设计一个API或者组件时,最佳的方式就是先设计她的使用场景,从行为开始,对该API或者组件进行描述,最后再将缺失的“实现”部分补全就能够了。

假设咱们将在上一章中的AppComponent里使用这个新的component,根据以前的需求描述,咱们的使用场景应该是这个样子的 :

import {Component, OnInit} from '@angular/core';

import {Item} from './CheckableItem';

//该component使用checkable-item做为selector
//并能够经过[item]属性传入一个object
//还能够经过(onItemClicked)接受一个点击事件
@Component({
    selector: 'my-app',
    template: `
    <h1>My First Angular 2 App</h1>
    <checkable-item [item]="itemInfo" (onItemClicked)="toggle($event)">
    </checkable-item>
    `
})
export class AppComponent implements OnInit {

    itemInfo: Item;

    //当实现OnInit接口时,必须重写ngOnInit方法
    //关于OnInit,详见:
    //https://angular.io/docs/ts/latest/guide/lifecycle-hooks.html#!#hooks-overview
    ngOnInit() {
        //设定初始值
        //根据需求第1条,包含两个属性
        this.itemInfo = {
            isChecked: false,
            txt: 'Hello World!'
        };
    }

    //根据需求第3条,点击component后,事件要
    //冒泡到父组件(调用方)
    toggle(item: Item) {
        //当获取到CheckableItem的点击事件时,
        //给itemInfo从新赋值,并将isChecked置反
        //注:从新赋值是根据需求第4条的不可变性
        this.itemInfo = {
            isChecked: !item.isChecked,
            txt: item.txt
        };
    }
}

实现component

根据上述介绍,再结合以前的效果图,咱们要作的固然就是一个标准的Component。她有template,而且包含了至少一个input和一个label标签。

有了使用场景(行为),接下来就是实现这个CheckableItem了:

touch ts/CheckableItem.ts

向刚建立的ts/CheckableItem.ts文件里写入以下内容:

import {Component, Input, Output, EventEmitter, ChangeDetectionStrategy} from '@angular/core';

@Component({
    //脏检查策略,OnPush指当且仅当传入参数的reference发生变动时
    //触发组件重绘。这和React中的shouldComponentUpdate殊途同归,
    //不过更先进(由于React仍是须要手动实现的)
    //这也是上一步里itemInfo必须从新赋值的缘由
    changeDetection: ChangeDetectionStrategy.OnPush,
    selector: 'checkable-item',
    //仅在当前component做用域下有效的class
    styles: [`
        .deleted{
            text-decoration: line-through;
        }
    `],
    //template就如咱们需求里的描述那样,由一个input标签和
    //一个label标签组成
    template: `
    <div>
        <input type="checkbox" (change)="clickItem($event)">
        <label [class.deleted]="item.isChecked">{{ item.txt }}</label>
    </div>
    `
})
export class CheckableItem {
    //item被声明为Input,即会在父组件传入参数时用到
    @Input() item: Item;
    //onItemClicked被声明为Output,用来在用户点击input标签
    //时向上冒泡事件
    @Output() onItemClicked = new EventEmitter();

    //监听input上的click事件,当用户点击时,首先阻止默认行为
    //由于是否变化(重绘)是由父组件决定的
    //而后冒泡点击事件
    clickItem(e: MouseEvent) {
        e.preventDefault();
        this.onItemClicked.emit(this.item);
    }
}


export interface ToggleItemHandler {
    (item: Item): void;
}

export interface Item {
    isChecked?: boolean;
    txt?: string;
}

有朋友看到这里,对[]()之类的绑定标签表示不解,这里咱们统一来解释:

  1. [target] = "expression",将右边表达式对应的值绑定到左边的target。譬如:在ts/app.ts里,咱们使用[item]="itemInfo"itemInfo对应的值绑定到了组件CheckableItemitem上,这样,在CheckableItem里就能够经过this.item获取到父组件传进来的参数了。

  2. (target) = "statement",将左边的事件传递给了右边的表达式(一般就是事件处理函数)。譬如:在ts/app.ts里,咱们使用(onItemClicked)="toggle($event)"CheckableItem冒泡上来的onItemClicked事件传递给了toggle函数。

  3. [class.deleted]="item.isChecked",是class的一种特殊用法,指当item.isChecked表达式为真时,为该标签的class里增长deleted;反之,则删除该标签class里的deleted

引入声明

打开以前写的index.ts,增长CheckableItem引入:

import 'core-js/es6';
import 'core-js/es7/reflect';
import 'zone.js/dist/zone';

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

//引入CheckableItem
import {CheckableItem} from './CheckableItem';

@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, CheckableItem ],//引入声明
  bootstrap:    [ AppComponent ]
})
class AppModule { }

platformBrowserDynamic().bootstrapModule(AppModule);

见证奇迹

OK,事已至此,咱们是否是又该启动一把程序看看效果了?

npm start

你又看到了伟大的效果:

图片描述

下回预告:小刀升级 - 多component协做

相关文章
相关标签/搜索