目錄css
1.hello world!html
2.配置開發環境git
源代碼下載angularjs
連接: https://pan.baidu.com/s/1i5pGloT 密碼: g7ubgithub
注意:這一小節的內容,並不是生產環境的作法,讀者能夠沒必要操做,看演示就行了。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強烈推薦使用。
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 ); } };
第三,分析一下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
開發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>
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();}