Angular2 簡介

jopen 9年前發布 | 43K 次閱讀 Angular2 Web框架 angularjs

Angular2 簡介

聲明:Angular2 現在還處于開發預覽版,所以會有很多功能缺失,非兼容性升級,并且也會存在各種變化。

那么準備好了么?Yeah!

在這個實例中,我們需要用 german words 編寫一個應用。如果用戶通過認證的話就將這個單詞解碼。

第一步,從這里把后端服務KOA抓下來,然后執行:

$ npm install
$ node server.js

注意:你需要 node 0.11+ 版本或者 io.js 來運行 koa。如果選擇 node 的話,需要用 --harmony 來啟動服務:

$ node --harmony server.js

我們邊啟動服務邊把這個庫 clone 下來,切到 before 目錄下,使用命令:

$ npm install
$ npm start

我從我的朋友Pawel那里搞到了這個代碼模板然后修改了一下,同時也感謝mgontogdi2290

讓我來簡單解釋一下:

由于 Angular 2 還不是最終版本,所以它需要配置一大堆模板代碼,而這個框架就是做這個的。

一方面,我們用經典的 javascript 和 css 任務流管理工具 gulpfile 來處理angular 和它的路由規則。另一方面,我們用 index.html 來加載所有我們需要的Angular 2 的庫。如我之前所說,在 Angular 2 發布最終版之前, 我們需要一大堆庫來運行 Angular 2, 但不用擔心,最終版是不需要這么麻煩的。

另一個有趣的東西是 System 模塊。System 是ES6的模塊加載器,也是我們的應用啟動模塊。耶,再也不需要一大堆script標簽了,耶!

當然,也不會再有 ng-app 了 : )

那么,System 模塊應該加載 index 來啟動我們的應用程序,對吧?那么該怎么做呢?想要啟動 Angular 2 應用,我們需要將入口組件傳入 bootstrap(譯者注:bootstrap)方法。那入口組件是什么玩意?我們馬上就會知道了 :)

bootstrap(OurMainComponent);

在我們的應用程序中,需要創建一個App組件來引導啟動我們的應用程序:

index.js

import { bootstrap } from 'angular2/angular2'; import { App } from './app/app';

bootstrap(App);</pre></div>

我們只需要導入我們的 App 組件和 bootstrap 方法,然后就可以使用 bootstrap 方法啟動應用了。我們的應用還需要引入路由(譯者注:router)組件,由于路由組件是 angular 2 的外部組件,所以我們也需要把它作為依賴加載進來。直到今天(2015-05-04),路由模塊也還不能導出必要的注入使其更加易用,所以我們需要構造一個新的路由實例然后將它注入進去:

index.js

import { bootstrap } from 'angular2/angular2'; import { RootRouter } from 'angular2/src/router/router'; import { Pipeline } from 'angular2/src/router/pipeline'; import { bind } from 'angular2/di'; import { Router } from 'angular2/router';

import { App } from './app/app';

bootstrap(App, [ bind(Router).toValue(new RootRouter(new Pipeline())) ]);</pre></div>

我們加載了所有構造路由需要的依賴,并且用 bind(譯者注:bind)服務創建了一個路由的綁定。希望能盡早修復這個問題。

好了,現在讓我們正式開始寫 App 組件的代碼吧。不過,組件到底是什么?組件只是一個類,可以用來表示一個 home 頁面,或者一個 login 模塊,users 信息模塊……或者可以用來創建一個類似于 datepicker,tabs 等的指令(譯者注:指令)。

app/app.js

export class App {

}</pre></div>

如我所說,組件只是一個類(我們把它導出以便于可以在其他文件中導入使用,就像我們在 index 中導入其他組件一樣)。到目前為止好像我們還是沒都沒做,讓我們動起來!

在 Angular 2 中我們可以使用注解(譯者注:[annotations][10])。想象一下通過注解的方式給我們的類添加元數據。我們一步一步來。第一步,導入我們需要的兩個注解:

app/app.js

import {View, Component} from 'angular2/angular2';</pre></div>

然后我們就可以使用了,Component 注解用來給組件自身添加元數據,包括組件的選擇器,以及需要注入的服務。View注解用來添加 HTML 模板,我們可以給組件指定想要使用的模板,以及指令等。我們可以加入多個 View 注解(mobile 視圖,desktop 視圖等)。

讓我們用起來:

app/app.js

@Component({ selector: 'words-app' }) @View({ template: &lt;h1&gt;Hello angular 2&lt;/h1&gt; }) export class App {

}</pre></div>

我們給 Component 指定了組件的選擇器 words-app(唉呀媽呀,終于不用再糾結像Angular 1 里面是駝峰命名還是下劃線命名的問題了),這意味著如果我們想在任何一個地方用這個組件,只需要寫上<words- app></words-app>就可以了。

這次我們先給 View 創建一個簡單的模板【注意引號(譯者注:這里也可以用單引號,雙引號)】。

注意:不要在注解后面加分號,那會把Angular 2搞哭的 :)

你說過可以把這個選擇器放在任何地方?耶,我們來修改一下 index.html:

index.html

<body> <div class="container"> <words-app> Loading... </words-app> </div> </body></pre></div>

好了,現在我們來運行下我們的組件,在瀏覽器中訪問 localhost:3000

到目前為止,一切都還好,對吧?現在我們需要配置一下從 bootstrap 函數中注入的路由。

app/app.js

export class App { constructor(router: Router) {

} }</pre></div>

構造函數會接收一個 Router 類型的參數 router:

app/app.js

export class App { constructor(router: Router) { router .config('/home', Home) .then(() => router.navigate('/home')); } }</pre></div>

router 有一個 config 方法,我們可以通過傳遞 path 為組件配置路由規則,然后通過鏈式調用 promise 的方式將路由導航到 /home。

不久以后,我們就可以用注解的方式為每個組件配置路由了。不過現在我們還是得用這種方式配置路由。

當我們通過一個新的路由加載組件時,組件生成的 View 放在哪里呢?我們需要為這個新的組件添加一個叫做 router-outlet的 ng-view。

我們把 View 的注解稍作修改:

app/app.js

@View({ template: &lt;router-outlet&gt;&lt;/router-outlet&gt;, directives: [RouterOutlet] })</pre></div>

這一點非常重要:當模板用到指令/組件的時候,我們需要把 RouterOutlet 導入進來。注意,我們把導入的 RouterOutlet對象添加到了 directives 數組中。說到導入…… 我們還需要導入幾個組件。導入這幾個組件后,我們的代碼如下:

app/app.js

import {View, Component} from 'angular2/angular2'; import {Router, RouterOutlet} from 'angular2/router'; import {Home} from '../home/home';</pre></div>

我們加載了 Router, RouterOutlet 和 Home 組件。

現在我們的應用會直接跳轉到 Home 組件。我們來寫這個組件:

home/home.js

import {View, Component} from 'angular2/angular2';

@Component({ selector: 'home' }) @View({ templateUrl: 'home/home.html' }) export class Home {

}</pre></div>

這次我們用引用外部文件的方式代替直接在注解中嵌入模板的方式:

home/home.html

<h1>This is home</h1></pre></div>

在啟動我們的應用之前,我們把 bootstrap 引入到我們的 index.css 中:

index.css

@import 'bootstrap';</pre></div>

現在我們的應用看起來應該是這樣的:

現在我們來做點兒有用的東西?在這個 home 組件中,我們希望每次點擊按鈕的時候都可以獲取一個新的單詞。我們需要創建一個 Words 的服務來處理請求和數據:

services/words.js

export class Words { getWord() { var jwt = localStorage.getItem('jwt');

return fetch('http://localhost:3001/api/random-word', {
  method: 'GET',
  headers: {
    'Accept': 'application/json',
    'Content-Type': 'application/json',
    'Authorization': 'bearer ' + jwt
  }
})
.then((response) => {
  return response.text();
})
.then((text) => {
  return JSON.parse(text);
})

} }</pre></div>

服務在 Angular 2 中只是一個類,對于這個 Words 服務而言,我們只需要一個獲取單詞的方法而已。到今天為止,Angular 2 還沒有一個類似于 $http 的庫,所以我們需要使用一個名為 fetch 的庫。

我們使用 fetch 向服務端發起一個請求,如果有 JWT 令牌的話解析之,我們從服務器端響應中提取返回的文本并解析成JSON 對象,然后再下次調用 .then 方法的時候,我們就能拿到這個 JSON 對象。

我們在 Home 中導入這個服務:

home/home.js

import {Words} from '../services/words';</pre></div>

現在我們需要在組件中注入這個服務:

home/home.js

@Component({ selector: 'home', injectables: [Words] })</pre></div>

最后,我們給構造函數傳入這個服務的實例對象:

home/home.js

export class Home { constructor(words: Words) { this.words = words; } }</pre></div>

就像 router 對象,我們得到一個 Words 類型的實例 words。

服務代碼做好之后,現在我們來編寫模板代碼:

home/home.html

<div class="jumbotron centered"> <h1>German words demo!</h1> <p>Click the button below to get a random German word with its translation:</p> <p><a class="btn btn-primary" role="button" (click)="getRandomWord()">Give me a word!</a></p> <div *if="word"> <pre>Word: {{word.german}}</pre> </div> </div></pre></div>

這里有幾個 Angular 2 的新語法糖。我們用(click)代替了 angular 1 中的 ng-click。圓括號表示這是一個事件(點擊事件)。另一個語法糖,*if 就是我們經典的 ng-if。符號*表示這是一個模板,一般用來做一些簡寫,相當于:

<template [if]="word">

那 if 是一個指令(譯者注:指令)么?是的。如果我們需要在模板中使用指令的話,之前怎么說來著?嗯,我們需要導入這些玩意:

home/home.js

import {View, Component, If} from 'angular2/angular2';</pre></div>

然后:

home/home.js

@View({ templateUrl: 'home/home.html', directives: [If] })</pre></div>

下一步,我們需要在點擊 button 的時候顯示服務器端返回的單詞:

home/home.js

getRandomWord() { this.words.getWord().then((response) => { this.word = response; }); }</pre></div>

再試著運行一下我們的應用,我們會看到:

嗯,終于像那么回事兒了!

現在我們來做個身份認證。你說是一個服務?沒錯!

export class Auth {
  constructor() {
    this.token = localStorage.getItem('jwt');
    this.user = this.token && jwt_decode(this.token);
  }

isAuth() { return !!this.token; }

getUser() { return this.user; }

login(username, password) { return fetch('http://localhost:3001/sessions/create', { method: 'POST', headers: { 'Accept': 'application/json', 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }) }) .then((response) => { return response.text(); }) .then((text) => { this.token = JSON.parse(text).id_token; localStorage.setItem('jwt', this.token); }); }

logout() { localStorage.removeItem('jwt'); this.token = null; this.user = null; } }</pre></div>

我們存儲兩個字段,一個是令牌,一個是解碼后的令牌,然后我們需要一個 login 方法和logout 方法來做登錄和登出。沒什么特別的東西。localStorage 和 jwt_decode 都是全局的,不需要導入。我們再次使用 fetch 函數在服務器端處理請求。

另外,既然我們說到了登錄,那么我們就需要一個登錄的途徑,對吧?我們來創建一個登錄組件:

login/login.js

import {Component, View} from 'angular2/angular2'; import {Router} from 'angular2/router'; import {Auth} from '../services/auth';

@Component({ selector: 'login', injectables: [Auth] }) @View({ templateUrl: 'login/login.html' }) export class Login { constructor(router: Router, auth: Auth) { this.router = router; this.auth = auth; }

login(event, username, password) { event.preventDefault(); this.auth.login(username, password).then(() => { this.router.parent.navigate('/home'); }) .catch((error) => { alert(error); }); } }</pre></div>

就像其他組件,我們需要 Component 和 View 等注解,還需要注入一個叫做 Auth 的新服務。

這個組件只有一個方法:用 Auth 服務處理登錄并且把 jwt 令牌塞到 localStorage 里面。如果成功的話,我們就跳轉到/home 頁面。

編寫我們的模板代碼:

login/login.html

<div class="login jumbotron center-block"> <h1>Login</h1> <form role="form" (submit)="login($event, username.value, password.value)"> <div class="form-group"> <label for="username">Username</label> <input type="text" #username class="form-control" id="username" placeholder="Username"> </div> <div class="form-group"> <label for="password">Password</label> <input type="password" #password class="form-control" id="password" placeholder="Password"> </div> <button type="submit" class="btn btn-default">Submit</button> </form> </div></pre></div>

注意我們用 #username 代替了 ng-model="username" 。這是 Angular 2 中的綁定方式。

最后一個步,我們簡單修改一下 css,因為表單稍微有點寬:

login/login.css

.login { width: 40%; }</pre></div>

同樣需要導入這些 .css 文件:

index.css

@import 'bootstrap'; @import './src/login/login.css';</pre></div>

為了展示 login 組件,我們需要與 Home 組件建立連接:

home/home.html

<div class="jumbotron centered"> <h1>German words demo!</h1> <p>Click the button below to get a random German word with its translation:</p> <p><a class="btn btn-primary" role="button" (click)="getRandomWord()">Give me a word!</a></p> <div if="word"> <pre>Word: {{word.german}}</pre> </div> </div> <div if="isAuth"> <p>Welcome back {{user.username}}</p> <a href="#" (click)="logout($event)">Logout</a> </div> <div *if="!isAuth"> <a href="#" (click)="login($event)">Login</a> </div></pre></div>

我們有了一個名為 isAuth 的標識,可以讓我們在兩個 div 之間進行切換。

我們也需要將 Auth 服務導入進來:

home/home.js

import {Auth} from '../services/auth';

@Component({ selector: 'home', injectables: [Words, Auth] })</pre></div>

然后我們需要 login 和 logout 方法,isAuth 標志值,并將其關聯到我們的用戶。現在我們的組件是這樣的:

home/home.js

export class Home { constructor(router: Router, words: Words, auth: Auth) { this.router = router; this.auth = auth; this.words = words;

this.isAuth = auth.isAuth();

if (this.isAuth) {
  this.user = this.auth.getUser();
}

}

getRandomWord() { this.words.getWord().then((response) => { this.word = response; }); }

login(event) { event.preventDefault(); this.router.parent.navigate('/login'); }

logout(event) { event.preventDefault(); this.auth.logout(); this.isAuth = false; this.user = null; } }</pre></div>

在不久的將來,就像基礎組件 ui-sref 一樣,RouteLink 組件可以讓我們避免 login 這種做法。

好的,現在讓我們鏈接到 login 頁面:

然后我們點擊 login,我們就會……哦 稍等,它沒反應了。 啊,我們忘了把 login 的路由加到 app.js 里:

app/app.js

import {Login} from '../login/login';

export class App { constructor(router: Router) { router .config('/home', Home) .then(() => router.config('/login', Login)) .then(() => router.navigate('/home')); } }</pre></div>

現在它好了:

然后我們用示例賬戶(demo / 12345)登錄,我們會看到:

爽!

最后一步,當我們登錄進去之后我們需要把單詞解碼,這個比較簡單。我們已經從服務器取到了單詞,并且現在服務器認為我們處于登錄狀態,所以它也會把解碼后的單詞返回回來。這意味著我們只需要更新我們的模板就可以了:

home/home.html

<div class="jumbotron centered"> <h1>German words demo!</h1> <p>Click the button below to get a random German word with its translation:</p> <p><a class="btn btn-primary" role="button" (click)="getRandomWord()">Give me a word!</a></p> <div if="word"> <pre>Word: {{word.german}}</pre> <pre if="isAuth">Translation: {{word.english}}</pre> <p if="!isAuth">Please login below to see the translation</p> </div> </div> <div if="isAuth"> <p>Welcome back {{user.username}}</p> <a href="#" (click)="logout($event)">Logout</a> </div> <div *if="!isAuth"> <a href="#" (click)="login($event)">Login</a> </div></pre></div>

現在我們終于完成了應用:

噢 稍等,我忘了登錄:

我希望你學習 Angular 2 能比學圖片中的單詞還要快 :)

在結束這篇文章之前,作為一個有趣的探索,你可以把 Login 組件導入到 Home 中,然后把 Login 作為一個指令加入到View 的指令數組中,接著把 <login></login> 添加到模板的底部。這樣做之后,你就可以在的 Home 頁面使用 login 組件完整的表單和功能了,耶!

我想感謝Matias GontovnikasPatrickJS的demo angular2-authentication-sample和他們的解釋,這給我理解整個流程提供了巨大的幫助。

Jesus Rodriguez 發表于 2015年5月4日

原文地址:An introduction to Angular 2

外刊君推薦閱讀:

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!