邁向angularjs2系列(2):angular2指令詳解

目錄css

1.hello world!html

2.配置開發環境git

源代碼下載angularjs

連接: https://pan.baidu.com/s/1i5pGloT 密碼: g7ubgithub

一:helloworld!

注意:這一小節的內容,並不是生產環境的作法,讀者能夠沒必要操做,看演示就行了。typescript

爲了簡單快速的運行ng2程序,那麼引入直接angular2版本和頁面的基本框架。express

index.html:npm

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
     <app></app>
     <!--使用app組件-->
     <script src="https://code.angularjs.org/2.0.0-beta.9/angular2-polyfills.min.js">

     </script>
     <script src="https://code.angularjs.org/2.0.0-beta.9/Rx.umd.min.js">

     </script>
     <script src="https://code.angularjs.org/2.0.0-beta.9/angular2-all.umd.min.js">

     </script>
     <script src="./app.js"></script>

</body>
</html>

app.js:json

var App=ng.core.Component({
    //定義了名稱爲App的組件。
    selector:"app",
    //匹配全部的app標籤
    template:"<h1>hello  {{target}}</h1>"
})
.Class({
    //Class函數傳遞了一個對象字面量,只有constuctor方法
    constructor:function(){
        this.target="world";
    }
});
ng.platform.browser.bootstrap(App);
//ng.platform.browser是命名空間

直接打開index.html,那麼html顯示爲:gulp

結論:並無用到typescript,因此它不是必須的,但ng2強烈推薦使用。

二: 配置開發環境和angular2的typescript實現

1.經過git克隆項目

step1:安裝git

到網站下載exe文件,https://github.com/git-for-windows/git/releases/download/v2.13.2.windows.1/Git-2.13.2-64-bit.exe。安裝的過程我只是改了一下安裝目錄。

安裝目錄:

檢測git是否安裝正確:

首先在開始菜單裏找到git cmd(也能夠吧快捷方式發送到桌面),而後輸出命令git --version。

step2:進入本身的項目目錄,運行命令 git clone https://github.com/mgechev/switching-to-angular2.git 。

已經克隆下來。good!

step3:進入項目目錄,模塊安裝,而後啓動Server。

$ npm install ;
$ npm start;//啓動server

瀏覽器顯示結果爲:

success

step4:上手試玩(optional)

內容替換,switching-to-angular2/app/ch4/ts/hello-world/app.ts:

import {Component} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';

@Component({
  selector: 'app',
  templateUrl: './app.html'
})
class App {
  target:string;
  constructor() {
    this.target = 'world';
  }
}

bootstrap(App);

瀏覽器顯示結果爲:

2.hello-world深度解析

注意:代碼位於switching-to-angular2/app/ch4/ts/hello-world目錄)

首先,看一下一共有4個文件。

index.html負責hello-world的首頁(默認)文件的顯示,app.html負責app模板的內容,meta.json是一些元信息,app.ts負責js代碼邏輯。

接下來詳細看index.html。

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title><%= TITLE %></title>
  <!--TITLE變量注入-->
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- inject:css -->
  <!-- endinject -->
</head>
<body>

  <app>Loading...</app>
  <!--app組件-->
  <!-- inject:js -->
  <!-- endinject -->
  <%= INIT %>
  <!--INIT是變量注入-->
</body>
</html>

app標籤有文本內容,"Loading"一直處於可見狀態,直到應用啓動好、主組件渲染完畢爲止。而 <%= TITLE %> 和 <%= INIT %> 是用來注入變量的。這些變量是在哪裏定義的呢?

 ,傳授一個全局搜索文本的方法:

step1:文件目錄右鍵,選擇find in path

step2:搜索"TITLE"。

因此,它的定義在switching-to-angular2/tools/tasks的build.index.ts文件中。

build.index.ts:

import {join, sep} from 'path';
import {APP_SRC, APP_DEST, DEPENDENCIES, SYSTEM_CONFIG, ENV} from '../config';
import {transformPath, templateLocals} from '../utils';

export = function buildIndexDev(gulp, plugins) {
  return function () {
    return gulp.src(join(APP_SRC, '**', 'index.html'))
      // NOTE: There might be a way to pipe in loop.
      .pipe(inject())
      .pipe(plugins.template(
        require('merge')(templateLocals(), {
          TITLE: 'Switching to Angular 2',
          INIT: `
<script>
  System.config(${JSON.stringify(SYSTEM_CONFIG)});
  System.import("./app")
    .catch(function () {
      console.log("Report this error to https://github.com/mgechev/switching-to-angular2/issues", e);
    });
</script>`
        })
      ))
      .pipe(gulp.dest(APP_DEST));
  };


  function inject() {
    return plugins.inject(gulp.src(getInjectablesDependenciesRef(), { read: false }), {
      transform: transformPath(plugins, 'dev')
    });
  }

  function getInjectablesDependenciesRef() {
    let shims = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === 'shims');
    let libs = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === 'libs');
    let all = DEPENDENCIES.filter(dep => dep['inject'] && dep['inject'] === true);
    return shims.concat(libs).concat(all).map(mapPath);
  }

  function mapPath(dep) {
    let prodPath = join(dep.dest, dep.src.split(sep).pop());
    return ('prod' === ENV ? prodPath : dep.src );
  }
};
build.index.ts

第三,分析一下ch4/ts/hello-world/app.ts。

import {Component} from '@angular/core';
//導入了component裝飾器
import {bootstrap} from '@angular/platform-browser-dynamic';
//導入了bootstrap函數
@Component({
  //Component裝飾了class App
  selector: 'app',
  templateUrl: './app.html'
  //對象字面量參數和ES5相似,app選擇器和視圖內容。模板既能夠template內聯,也可使用url,和ng1相似。
})
class App {
  target:string;
  constructor() {
    this.target = 'world';
  }
}
bootstrap(App);
//啓動APP

四: angular2指令詳解

開發app組件如今開始了!

1.基礎to-do list應用

(1)運行代碼:

進入switchingToNG2/switching-to-angular2目錄,運行npm start,那麼打開瀏覽器,進入紅色框的連接。

 

(2)進入switching-to-angular2/app/ch4/ts/ng-for/detailed-syntax目錄,一共4個部分。

app.ts:

import {Component} from '@angular/core';
import {bootstrap} from '@angular/platform-browser-dynamic';
//導入
@Component({
  //裝飾器
  selector: 'app',
  templateUrl: './app.html',
})
class App {
  todos:string[];
  name:string;
  constructor() {
    //app類的屬性定義,能夠直接在app組件的代碼裏使用
    this.name = "愛莎";
    this.todos = ['呼風喚雪', "保護妹妹"];
  }
}

bootstrap(App);
//啓動應用

app.html:

<h1>你好,{{name}}!</h1>
<h3>
  to-do清單:
</h3>
<ul>
  <template ngFor let-todo [ngForOf]="todos">
    <li>{{todo}}</li>
  </template>
</ul>

說明:template標籤內部能夠放HTML片斷。template的做用是屏蔽了瀏覽器的直接渲染,交給模板引擎來處理。

index.html:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <title><%= TITLE %></title>
  <meta name="description" content="">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <!-- inject:css -->
  <!-- endinject -->
</head>
<body>

  <app>Loading...</app>
  <!-- inject:js -->
  <!-- endinject -->
  <%= INIT %>
</body>
</html>
默認頁面相似以前DEMO的內容

meta.json:

{
  "title": "List of items (detailed syntax)",
  "description": "List of items using explicit template for ng-for",
  "id": 3,
  "presented": true
}

這裏的title在index.html中有使用到。

(3)結果顯示。

打開http://localhost:5555/dist/dev/ch4/ts/ng-for/detailed-syntax/地址,顯示以下:

2. 更加優秀的ngFor指令

 ngFor用來遍歷數據,和ng1的ng-repeat相似,也更優秀。

<template ngFor let-todo [ngForOf]="todos">
    <li>{{todo}}</li>
  </template>

ng1的指令用法五花八門,須要對各類屬性值深刻去理解。而ng2引入了更加簡單的約定,語義性更高。

屬性有3種用法:

●propertyName="value"

第一種語法:普通字面量

propertyName接收字符串字面量,要使用引號哦。angular不會對它進行進一步處理。

●[propertyName]="expression"

第二種語法:方括號語法

提示angular2要當作表達式來處理。屬性包圍在方括號裏面,angular就會嘗試執行這個表達式,執行上下文就是模板對應的組件。

●(eventName)="handlerFunc()"

第三種語法:小括號語法

固然是事件綁定咯。

:) 能夠看到,誰是誰,很是清晰。

(1)模板如何使用ngFor指令

 <template ngFor let-todo [ngForOf]="todos"> [ngForOf]是遍歷的數據集合,let-todo(這裏是var-變量名語法)告訴ng2遍歷建立的新變量名字叫todo,類型是let。

(2)使用語法糖

語法糖,簡單的說就是更方便更簡潔的寫法。

使用星號,扔掉template標籤 ,指令也直接用到容器標籤上。

<ul>
  <li *ngFor="#todo of todos">{{todo}}</li>
</ul>

angular2會自動對上面代碼進行「脫糖」處理,就能變成上面比較囉嗦的形式了。

3.自定義angular2指令

 上一節講了如何在DOM標籤上使用內置指令,這一節講自定義指令。自定義纔是高級玩法。

本小節將構建一個簡單的tooltip自定義指令。

(1)運行代碼:

進入switchingToNG2/switching-to-angular2目錄,運行npm start,那麼打開瀏覽器,進入紅色框的連接。

(2)進入目錄switching-to-angular2/app/ch4/ts/tooltip,                                                                                                                                                                                                      

app.html:

<div saTooltip="Hello world!">
</div>

ch4/ts/tooltip/app.ts:

一共分爲導入、Overlay類、指令、常規的APP類定義。

//導入
import {HostListener, Input, Injectable, ElementRef, Inject, Directive, Component} from '@angular/core';

import {bootstrap} from '@angular/platform-browser-dynamic';

//Overlay類的定義
class Overlay {
  private el: HTMLElement;
  constructor() {
    var el = document.createElement('div');
    el.className = 'tooltip';
    this.el = el;
  }
  close() {
    this.el.hidden = true;
  }
  open(el, text) {
    this.el.innerHTML = text;
    this.el.hidden = false;
    var rect = el.nativeElement.getBoundingClientRect();
    this.el.style.left = rect.left + 'px';
    this.el.style.top = rect.top + 'px';
  }
  attach(target) {
    target.appendChild(this.el);
  }
  detach() {
    this.el.parentNode.removeChild(this.el);
  }
}
//指令定義
@Directive({
  selector: '[saTooltip]'
})
export class Tooltip {
  @Input()
  saTooltip:string;

  constructor(private el: ElementRef, private overlay: Overlay) {
    this.overlay.attach(el.nativeElement);
  }
  @HostListener('mouseenter')
  onMouseEnter() {
    this.overlay.open(this.el, this.saTooltip);
  }
  @HostListener('mouseleave')
  onMouseLeave() {
    this.overlay.close();
  }
}
//APP類定義,並被component裝飾器修飾以及最後的啓動
@Component({
  selector: 'app',
  templateUrl: './app.html',
  providers: [Overlay],
  directives: [Tooltip]
})
  
class App {}

bootstrap(App);

(3)結果顯示。

打開http://localhost:5555/dist/dev/ch4/ts/tooltip/地址,那麼會有以下結果:

(2)導入的類HostListener、Directive、ElementRef

app.ts導入了HostListener, Input, Injectable, ElementRef, Inject, Directive, Component這些類。

●HostListener(eventname)

用於事件處理。指令實例化的時候,angular2會把這個裝飾過的方法當成宿主標籤上對應eventname的事件處理函數。

@HostListener('mouseenter')
  //定義監聽器
  onMouseEnter() {
    this.overlay.open(this.el, this.saTooltip);
  }
  //定義監聽函數

●Directive

用於定義指令。用來添加額外的元數據。

//指令定義
@Directive({
  selector: '[saTooltip]'
  //大小寫敏感哦
})

●ElementRef

在宿主標籤裏注入其餘標籤的引用,不單單是DOM。好比這裏的模板就是angular包裝過的div標籤,裏面有tooltip屬性。

<div saTooltip="Hello world!">
</div>

(3)指令輸入的input裝飾器

接收的參數是須要綁定的屬性名。若是不傳遞參數,,默認綁定到同名屬性上。

注意:angular的HTML編譯器對大小寫敏感。

 咱們來看一下input的構造器到底幹了什麼?

@Input()
    //給指令定義一個saTooltip屬性,屬性的值是表達式。
  saTooltip:string;

(4)constructor構造器的理解

constructor(private el: ElementRef, private overlay: Overlay) {
    //overloay是定義的overlay類
    //咱們來看一下ElementRef到底是什麼
    console.log(el);
    this.overlay.attach(el.nativeElement);
  }

●私有屬性el

首先,看一下el,也就是ElementRef是什麼。根據打印結果,ElementRef就是app.html模板裏包裝好的div元素。

再來看attach。這行代碼負責剛剛的原生div元素添加內容。

attach(target) {
    target.appendChild(this.el);
    //target是原生的div,給它添加子元素,內容是overloay定義的this.el
  }

●私有屬性overlay

overlay的類型爲Overlay,也就是定義的class類。Overlay類實現了維護tooltip組件外觀的邏輯,並能夠用angular的DI依賴注入,固然要加上頂層組件Component的定義。

ch4/ts/tooltip/app.ts:

@Component({
  selector: 'app',
  templateUrl: './app.html',
  providers: [Overlay],//數組哦,實現Overlay的依賴注入
  directives: [Tooltip]
})

(5)封裝指令要在頂層組件聲明。

注意:聲明的是數組哦。整個組件的內部所有指令都會聲明在這裏。儘管顯式聲明有些麻煩,但能帶來更好的封裝性。而在ng1種全部指令都在全局命名空間中,壞處是容易命名衝突。同時咱們知道了組件用了哪些指令,更好理解了。

@Component({
//component裝飾器,傳遞對象字面量做爲參數
  selector: 'app',
  templateUrl: './app.html',
  providers: [Overlay],
  directives: [Tooltip]//聲明指令數組,angular編譯器就能夠發現tooltip指令了
})

class App {} 

五: 其餘,重命名指令的輸入輸出、語法糖的原本面目

先回顧語法糖怎麼書寫的:

@Directive(...)
class Dir{
  @Output()outputNmae=new EventEmitter();
  @Input() inputName;
}
//最佳實踐推薦的語法糖方式,更容易閱讀和理解

原本面目是:

@Directive({  outputs:['outputName:XXXX'],  inputs:['inputs:XXXX']})class Dir{  outputName=new EventEmitter();}
相關文章
相關標籤/搜索