├── advanced ├── custome form control.md ├── server side rendering.md ├── integrating-third-party-libraries.md └── template syntax.md ├── readme.md ├── fundamentals ├── 2-0-template-&-data-binding.md ├── cors with proxy config.md ├── Everything about Router.md ├── 0-setup.md ├── 3-forms.md ├── 5.The Root Module.md ├── Everything about http call.md ├── How to Form.md ├── 4-bootstraping.md ├── How to start a new Angular project.md ├── 2-1-displaying-data.md ├── RxJS in Angular.md ├── 7.User Input.md ├── 6.Displaying Data.md ├── 3-1-user-input.md ├── 9.Dependency Injection.md ├── 3-2-template-driven-forms.md ├── 3-4-reactive-forms.md ├── 1-architecture.md └── 10.Template Syntax.md ├── tricks.md └── learning.md /advanced/custome form control.md: -------------------------------------------------------------------------------- 1 | # Custom Form Controls in Angular 2 | Ref: [Custom Form Controls in Angular](https://blog.thoughtram.io/angular/2016/07/27/custom-form-controls-in-angular-2.html) 3 | -------------------------------------------------------------------------------- /advanced/server side rendering.md: -------------------------------------------------------------------------------- 1 | # 伺服器成像 2 | - [如何在 Angular CLI 建立的專案加入 Angular Universal 伺服器渲染功能](http://blog.miniasp.com/post/2017/06/18/How-to-setup-Angular-Universal-in-an-Angular-CLI-project.aspx) 3 | -------------------------------------------------------------------------------- /advanced/integrating-third-party-libraries.md: -------------------------------------------------------------------------------- 1 | # 整合第三方程式庫 2 | - [Angular 與 JQuery 共舞:整合第三方套件的技巧、陷阱與解決方案](https://www.facebook.com/will.fans/videos/1718120871550383),[(投影片)](https://www.slideshare.net/WillHuangTW/angular-jquery-3rd-party-js-library-tips-tricks) 3 | 4 | 5 | ### 整合 JQuery 到 angular 專案重點 6 | - 安裝 `jquery @types/jquery` 7 | - 在 typings.d.ts 中匯入 jquery 定義檔案。 8 | ```ts 9 | /// 10 | ``` 11 | - 進行撰寫程式碼和編譯時,應該就不會出現錯誤。 -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Angular 學習筆記 2 | Angular 學習筆記是一個希望蒐集、整理一些有關 Angular 的 **結 構 化** 的文章。就學習的角度而言,結構化資料能夠循序漸進、由淺入深地進行自學,讓學習者更能夠吸收並建立扎實有序的觀念。然而,台灣因為語言、市場、文化等種種因素,通常很難去提供這樣的學習資源。因此,希望透過這個小小的專案來達成這樣的目標。 3 | 4 | ## 現況 5 | 現有的文件都是我(t7yang)在自學 Angular 時邊讀官方的文件和網路上的教學,邊整理出來的資料(很多都未有最好的整理和歸納)。若希望可以讓台灣有一個以當地語言和用語更友善的學習 Angular 的環境,也歡迎一起貢獻。未來應該會轉換成組織的型式,以達到多作者都可以掛名。 6 | 7 | ## 目錄 8 | ``` 9 | / 10 | --learning.md 11 | 整理一些學習資源(可能參雜了一些非 Angular 的東西)。 12 | --/fundametals 13 | 主要是針對官方的基礎教材進行導讀(整理、重新解讀、補充其他資訊),非翻譯。 14 | --/angular advanced 15 | 除了官方指南之外的一些實務上碰到的情況或更進階的技巧整理。 16 | ``` 17 | -------------------------------------------------------------------------------- /fundamentals/2-0-template-&-data-binding.md: -------------------------------------------------------------------------------- 1 | # 模板與資料繫結 2 | 3 | MVC 或許是人們經常聽到的一個詞彙, MVC 的歷史沿革非常悠久。而且也是一個因應務實需求所產生的開發模式,意旨能夠將模型、視圖、控制器(model, view, controller)分開處理來達到更容易開發維護的目的。模型代表是資料(的模型)、視圖就是使用者看得到且可以被互動的介面、控制器著作為視圖與資料之間的溝通橋樑。 4 | 5 | 後續人們再根據不同的需求開發出 MVC 的變種, Angular 的開發模式也屬於 MVC 的變種,根據官方的說法是 MVW (模型、視圖、隨便啦)。了解這點後,再回過頭來看 Angular 的技術堆疊就比較好理解。 6 | 7 | - 在開發與語言 Angular 選擇了 TypeScript。 8 | - 模板仍然選擇 HTML 並加上自訂的模板語言。 9 | 10 | 因此要學好 Angular 的視圖開發,熟悉 HTML 和 Angular 自訂的模板語言就是不可或缺的部分。模板的技術又可以概略歸納為: 11 | 12 | - 模板語法 13 | - 內容 / 屬性的繫結 14 | - 事件的繫結 15 | - 結構控制 16 | - Pipe 17 | - 動畫 18 | - 元件的生命週期 19 | -------------------------------------------------------------------------------- /fundamentals/cors with proxy config.md: -------------------------------------------------------------------------------- 1 | # 用@angular/cli在本機開發時透過Proxy Config解決CORS的問題 2 | 來源:[如何在 Angular CLI 建立的 Angular 2 開發環境呼叫遠端 RESTful APIs](http://blog.miniasp.com/post/2017/02/05/Setup-proxy-to-backend-in-Angular-CLI.aspx) 3 | 4 | 1. 在專案目錄下建立一個proxy.config.json。 5 | ```json 6 | { 7 | "/api": { 8 | "target": "http://140.xxx.xxx.xxx", 9 | "secure": false 10 | } 11 | } 12 | ``` 13 | > - 實務上通常會設定為`/api`。 14 | > - `target`的部分則是遠端的API主機位置 15 | > 16 | > 最終我們只需要在服務中以`/api/signin`請求,則Proxy會自動幫我們導向`http://140.xxx.xxx.xxx/api/signin`。 17 | 18 | 2. 修改`package.json`裡面的`npm start`或自訂一個本機開發用的指令。 19 | ```json 20 | { 21 | "scripts": { 22 | "start": "ng serve --proxy-config proxy.config.json" 23 | } 24 | } 25 | ``` 26 | 27 | 3. 在服務中設定`basedUrl`,用剛剛設定`Proxy.config.json`中第一個KEY值做開頭。 28 | ```javascript 29 | export class SomeService { 30 | private basedUrl = '/api'; 31 | 32 | signin(): Observable { 33 | return this.http.post(`${basedUrl}/signin`, body, options).map().subscribe(); 34 | } 35 | } 36 | ``` 37 | 38 | 4. 停止並重新啟動`npm start`,就可以看到效果了。 39 | -------------------------------------------------------------------------------- /tricks.md: -------------------------------------------------------------------------------- 1 | # Angular 開發小技巧 2 | 3 | ## @angular/cli 4 | 5 | #### `export interface` 6 | 需要注意的是,因為JS本身並沒有`interface`的概念,因此就算TS定義並匯出`interface`,編譯後的檔案是不會產生任何程式碼的。這也導致有時時候會發生編輯匯入匯出`interface`後程式編譯失敗。這時只需要`ctrl + c`停止然後`npm start / yarn start`重新啟動編譯即可。 7 | 8 | #### `ng serve` 無法用 IP 連線 9 | `angular/cli` 為了某些考量,在預設的情況並不允許透過 IP 的方式連線至 `ng serve` 所啟動的伺服器。在某些情況下,要稍微妥協一下的作法是幫 `ng serve` 加上 `--host` 選項。 10 | 11 | ```json 12 | { 13 | "scripts": { 14 | "dev": "ng serve --host 0.0.0.0" 15 | } 16 | } 17 | ``` 18 | 19 | **注意**:上述的 `0.0.0.0` 請填入電腦的 IP。 20 | 21 | 來源:https://github.com/angular/angular-cli/issues/2542 22 | 23 | 24 | ## debugging 25 | - [Chrome Debugging with Angular CLI](https://github.com/Microsoft/vscode-recipes/tree/master/Angular-CLI) 26 | 27 | 28 | ## Component 29 | 30 | #### 動態產生 Component 31 | - [Dynamic component loading with Angular2: replace $compile](http://www.smartjava.org/content/dynamic-component-loading-angular2-replace-compile) 32 | - 用一般的 innerHTML 注入 raw HTML,再用事件回呼的方式攔截點選連結事件。 33 | 34 | 35 | ## Route 36 | 37 | #### title service 38 | - 如何實作動態網頁標題 - [Dynamic page titles in Angular 2 with router events](https://toddmotto.com/dynamic-page-titles-angular-2-router-events) 39 | -------------------------------------------------------------------------------- /fundamentals/Everything about Router.md: -------------------------------------------------------------------------------- 1 | # 一切有關路由器的資訊(Router) 2 | 3 | ## 路由守衛(Router Guard) 4 | 5 | ### 守衛的回傳值 6 | - 如果守衛回傳`true`則導向繼續處理。 7 | - 如果守衛回傳`false`則導向處理會被停止且使用者會留在原處。 8 | 9 | > 守衛還可以告訴路由器導向其他位置(譬如若在未登入的情況下,轉導向登入頁面)來有效取消目前的導向處理。 10 | 11 | 12 | ### 守衛應該回傳甚麼 13 | 上述提到了,就是`true`和`false`。但多數的情況通常需要等待向伺服器請求必要的資訊才能知道是否該回傳`true`或`false`。所以守衛的回傳結果可以是`Observable`或`Promise`。路由器會自動等待結果再導向處理應該往前或停止。 14 | 15 | 16 | ### 守衛的類型 17 | 1. `CanActivate`用於決定是否可以導向某個路由。 18 | 2. `CanActivateChild()`用於決定是否可以導向某個子路由。 19 | 3. `CanDeactivate`用於決定是否可以離開目前的所在的路由。 20 | 4. `Resolve`用於在路由正式被啟動前執行取得路由所須資料。 21 | 5. `CanLoad`用於決定是否可以導向某個以非同步載入的功能模組。 22 | 23 | Guard|類型|回傳 24 | --|---|-- 25 | CanActivate|Service|Observable / Promise / boolean 26 | CanActivateChild|Service|Observable / Promise / boolean 27 | CanDeactivate||Observable / Promise / boolean 28 | Resolve| |Observable / Promise / boolean 29 | CanLoad| |Observable / Promise / boolean 30 | 31 | ### 守衛的行為模式 32 | `http://localhost:3200/home/admin/dashboard/users` 33 | 34 | <<======== 從最深的子路開始檢查到最上層路由 ======== 35 | 36 | - `CanDeactivate()` 37 | - `CanActivateChild()` 38 | 39 | ======== 從最上層路由開始檢查到最深的子路 ========>> 40 | 41 | - `CanActivate()` 42 | 43 | 當有非同步請求載入功能模組時,會檢查`CanLoad()`。如果任何一個守衛回傳了`false`,尚未處理的守衛會全部被取消,整個導向處理就會被取消。 44 | -------------------------------------------------------------------------------- /fundamentals/0-setup.md: -------------------------------------------------------------------------------- 1 | ## Setup 2 | 導讀:[2.Setup](https://angular.io/docs/ts/latest/guide/setup.html) 3 | 4 | 5 | ### 快速開始新專案 6 | Angular專案的結構龐大,需要組態相當多的東西,若再考量到打包(bundle)的問題,就更加複雜。 7 | 所以建議直接以`@angular/cli`專案自動管理系統來協助我們開發Angular專案。 8 | 9 | ### CLI快速啟動 10 | 確保你的環境有node.js和npm。開啟終端機(terminal),輸入下列指令,你會看到類似的結果: 11 | ``` 12 | $ node -v 13 | $ v6.10.0 14 | $ npm -v 15 | $ 4.4.4 16 | ``` 17 | 18 | 安裝@angular/cli非常簡單,利用下列指令,進行全域安裝: 19 | ``` 20 | $ npm install -g @angular/cli 21 | ``` 22 | 23 | 建立和啟動伺服專案如下。建立的過程包含npm安裝,需要幾分鐘。 24 | ``` 25 | $ ng new my-app 26 | $ ... 27 | $ cd my-app 28 | $ ng serve --open 29 | ``` 30 | > ``` --open ```是自動開啟瀏覽器選項,可以不輸入,專案成案被伺服後,你可以從終端機看到伺服的網址。 31 | > 通常是 http://localhost:4200/。你也可以用``` ng serve --port=5000 ```來啟動不同埠號伺服環境。 32 | 33 | > ``` ng --help ```讓你可以快速瀏覽@angular/cli指令提供了那些選項、標旗(flag)、參數。 34 | 35 | 36 | ### ``` src ```資料夾 37 | 你主要的開發內容會放在``` src ```資料夾內,透過``` ng generate xxx ```你可以快速在``` src ```資料夾中建立諸如Component、service、Module、Pipe等Angular項目。 38 | 39 | 40 | ### 你還需要知道的事 41 | - 你知道yarn嗎?它被譽為效率更好的npm,透過設定@angular/cli的全域設定,你就可以讓新建立的專案使用yarn當作package管理器。 42 | ``` 43 | $ ng set --global packageManager=yarn 44 | - 更多有關@angular/cli產生的檔案結構,請參考導讀的連結。 45 | - 因為以TypeScript開發的關係,所以建議使用Visual Studio Code作為開發編輯器,同時安裝好一下套件: 46 | - Angular Language Service 47 | - 在模板提供智慧提示(Intellisense) 48 | - AngularDoc for Visual Studio Code 49 | - 在側邊欄自動執行ng xxx 指令,`ng generate Component` 50 | -------------------------------------------------------------------------------- /fundamentals/3-forms.md: -------------------------------------------------------------------------------- 1 | ## 表單/表格 2 | 3 | 表單是現代網站不可或缺的一個重要環節,尤其在 Web 2.0 之後強調使用者能夠參與,商業網站的交易行為都必須仰賴表單。 4 | 5 | 6 | ## HTML 表單 7 | 表單開發的基礎就是 HTML,下例是一個簡單範例: 8 | 9 | ```html 10 |
11 | 12 | 13 |
14 | ``` 15 | 16 | `
`這個標籤就代表一個表單,在標籤內會寫入控制項,最常使用的控制項就是``,它又會根據`type`屬性內容的不同產生不同資料類型的輸入框。[這裡](https://www.w3schools.com/html/html_form_elements.asp)可以查閱其他的表單控制項。 17 | 18 | ## Angular 表單 19 | 上述的說明相信已經可以建立對表單的基礎認識,但 Angular 表單是另外一個世界。 Angular 為了讓開發者能夠從容應付「雙向繫結」「驗證與狀態追蹤」「錯誤訊息」等開發者會碰到的問題,因此建立起 Angular 自己的表單架構。 20 | 21 | Angular 的表單基礎單元有三個,他們又分別繼承自`AbstractControl`這個抽象的控制項: 22 | - AbstractControl 23 | - FormControl(控制項) 24 | - FormGroup(控制組) 25 | - FormArray(控制陣列) 26 | 27 | 控制項是裡面最小的單位,跟 HTML 裡面的控制項相仿。控制組和控制陣列則屬於容器型的項目,兩者都可以承裝任何繼承自 AbstractControl 的項目,而且沒有層數的限制。 28 | 29 | 一般上基於資料模型,最外層通常是一個控制組,內部在根據資料模型不斷組合。下二例分別是一個簡單和複雜的例子。 30 | 31 | - FormGroup 32 | - FormControl 33 | - FormControl 34 | 35 | 36 | - FormGroup 37 | - FormControl 38 | - FormGroup 39 | - FormControl 40 | - FormArray 41 | - FormControl 42 | - FormGroup 43 | - FormControl 44 | - FormArray 45 | - FormGroup 46 | - FormControl 47 | 48 | 49 | ## 模板驅動表單與反應式表單 50 | 在接下來導讀中會陸續介紹到這兩者,這裡先說明在這兩種迥異的 Angular 表單設計模式裡面,只有反應式表單中開發者才會跟上述的基礎單元短兵相接。模板驅動表單因為結構較為鬆散、自由,而且很多事情已經由 Angular 背後幫開發者接管了,所以就算沒有建立起基礎單元概念也無妨。但若要上手反應式表單開發,則是必要搞懂這些基本單位關係。 51 | -------------------------------------------------------------------------------- /fundamentals/5.The Root Module.md: -------------------------------------------------------------------------------- 1 | ## 根模組 2 | 導讀:[5. The Root Module](https://angular.io/docs/ts/latest/guide/appModule.html) 3 | 4 | 5 | Angular 在瀏覽器初始化應用程式時,會去啟動(Bootstrap)根模組,這個根模組的根據慣例會被命名為 AppModule。這是一個典型的根模組。 6 | ```javascript 7 | import { NgModule } from '@angular/core'; 8 | import { BrowserModule } from '@angular/platform-browser'; 9 | import { AppComponent } from './app.Component'; 10 | 11 | @NgModule({ 12 | imports: [ BrowserModule ], 13 | declarations: [ AppComponent ], 14 | bootstrap: [ AppComponent ] 15 | }) 16 | export class AppModule { } 17 | ``` 18 | 19 | 你會發現在這個根模組的 Metadata(中繼資料/元資料/詮釋資料等)中也有一個其他模組不會有的成員```bootstrap```,因為對根模組而言,在初始化時也需要去啟動一個**根元件**。 20 | 21 | > 所以「根模組」和「根元件」都是應用程式初始化時對應邀啟動的項目。根元件在根模組的Metadata中指定,而根模組則是透過m```main.ts```中的```platformBrowserDynamic().bootstrapModule(AppModule);```所啟動。 22 | 23 | ```javascript 24 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 25 | import { AppModule } from './app/app.Module'; 26 | 27 | platformBrowserDynamic().bootstrapModule(AppModule); 28 | ``` 29 | 當啟動根模組後,根模組內的指定根元件也會被啟動,並把根元件的模板放置到```index.html```中由根元件所設定```selector```HTML 標籤中。若你使用`@angular/cli`,通常就會在```index.html```找到以下的內容: 30 | ```html 31 | 32 | Loading... 33 | ``` 34 | 35 | ### ```@NgModule()```讓 Class 變 Module 36 | 之前說過 Angular 的開發是 Class-based 的而且脫離不了 Decorator 和 ES Module。```@NgModule()```就是讓 Angular 知道這個 Class 是一個模組的關鍵。對根模組而言,在這個 Decorator 中,有三個最重要的成員: 37 | - imports: 匯入```BrowserModule```因為這個模組包含了許多瀏覽器環境中必須要用到的東西,譬如 Directive。 38 | - declarations: 宣告這個模組要用的項目,還記得需要被宣告的三兄弟是誰嗎?Component、Directive、Pipe。 39 | - boostrap: 指定讓 Angular 在初始化時要建立並插入到```index.html```的根元件。 40 | -------------------------------------------------------------------------------- /advanced/template syntax.md: -------------------------------------------------------------------------------- 1 | # 進階模板變數使用 2 | 3 | ## `as`暫存模板中的Async變數 4 | [ngIf 跟他的新朋友 else 和 then](http://blog.kevinyang.net/2017/04/19/angular-ngIf-else/) 5 | ```html 6 |
7 | {{ (user|async)?.name }} 8 |
9 | 10 |
11 | {{ _user.name }} 12 |
13 | ``` 14 | 15 | ## `*ngIf else then`的使用 16 | ```js 17 | @Component({ 18 | selector: 'ng-if-then-else', 19 | template: ` 20 | 21 | 22 | show = {{show}} 23 |
24 |
this is ignored
25 | Primary text to show 26 | Secondary text to show 27 | Alternate text while primary text is hidden 28 | ` 29 | }) 30 | class NgIfThenElse implements OnInit { 31 | thenBlock: TemplateRef = null; 32 | show: boolean = true; 33 | @ViewChild('primaryBlock') 34 | primaryBlock: TemplateRef = null; 35 | @ViewChild('secondaryBlock') 36 | secondaryBlock: TemplateRef = null; 37 | switchPrimary() { 38 | this.thenBlock = this.thenBlock === this.primaryBlock ? this.secondaryBlock : this.primaryBlock; 39 | } 40 | ngOnInit() { this.thenBlock = this.primaryBlock; } 41 | } 42 | ``` 43 | 44 | ## `ng-*`三兄弟 45 | - `ng-template` 46 | - 如同上述的例子,多數的情況下會跟`*ngIf`搭配使用。 47 | - `ng-content` 48 | - 自訂 Component 時做到 [transclude](https://toddmotto.com/transclusion-in-angular-2-with-ng-content) 的效果。 49 | - `ng-container` 50 | - 是為了避免使用模板語法時必須放置在一個 HTML 標籤中,進而破壞了 CSS 的設計。 51 | ```html 52 | value 53 | value 54 | ``` 55 | -------------------------------------------------------------------------------- /fundamentals/Everything about http call.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 網址參數 4 | 你可以手動的方式串聯好所有的網址參數再送出請求: 5 | ```js 6 | const queryString = `?search=${term}&action=opensearch&format=json&callback=JSONP_CALL`; 7 | 8 | return this.jsonp 9 | .get(wikiUrl + queryString) 10 | .map(response => response.json()[1]); 11 | ``` 12 | 13 | 或是你可以使用內建的`URLSearchParams`來建立網址參數: 14 | ```js 15 | import { URLSearchParams } from '@angular/http'; 16 | 17 | const params = new URLSearchParams(); 18 | params.set('search', term); 19 | params.set('action', 'opensearch'); 20 | params.set('format', 'json'); 21 | params.set('callback', 'JSONP_CALL'); 22 | 23 | return this.jsonp 24 | .get(wikiUrl, { search: params }) 25 | .map(response => response.json()[1]); 26 | ``` 27 | 28 | get方法預期的參數有:`get(url: string|Request, options?: RequestOptionsArgs)`,第二個參數是一個`RequestOptionsArgs`型別的物件,其中的`search`鍵內容會自動轉化成網址參數。利用`URLSearchParams`和`RequestOptionsArgs`會比你自己寫一長串的網址還要方便且更不容易出錯。 29 | 30 | 31 | ## `form-urlencoded` 的 Request Body 32 | 在某些形況下,可能會碰到後端無法接收(或開發者不習慣接收)`JSON.stringify`的 Request Payload。這時還是要送`application/x-www-form-urlencoded`的 Request Body,但每次都要用手轉一次嗎?這麼機械式的動作,其實可以用一個轉換函數來解決,然後在其他服務中匯入就好。 33 | 34 | ```js 35 | // shared.ts 36 | import { URLSearchParams } from '@angular/http'; 37 | 38 | export function GenerateBody() { 39 | return function generateBody(body: object): string { 40 | const result = new URLSearchParams(); 41 | Object.keys(body).forEach(key => result.set(key, body[key])); 42 | return result.toString(); 43 | }; 44 | } 45 | 46 | // logger.service.ts 47 | import { GenerateBody } from './shared'; 48 | 49 | export class LoggerService { 50 | 51 | private generateBody = GenerateBody(); 52 | 53 | somethingPost(toSend: Type): Observable { 54 | const body = this.generateBody(toSend); 55 | 56 | return this.http.post(`url`, body, headers) 57 | .map(...) 58 | .catch(...); 59 | } 60 | } 61 | ``` 62 | -------------------------------------------------------------------------------- /fundamentals/How to Form.md: -------------------------------------------------------------------------------- 1 | # Angular 表單和反應式表單(Reactive Form)範例 2 | 3 | ## 建立一個反應式表單和繫結在模板上 4 | - 建立一個`FormGroup`的實體 5 | - 透過自行匯入`FormGroup`的`Class`後`new`一個實體。或 6 | - 透過`form = FormBuilder.group({})`產生一個新的`FormGroup`。 7 | - 繫結到`
`中。 8 | - `
`內部`Control`以`formControlName`,子`FormGroup`以`fromGroupName`繫結: 10 | - `` 11 | - `
` 12 | 13 | 14 | ```javascript 15 | export class FormComponent { 16 | aFrom: FormGroup; 17 | 18 | constructor(private fb: FormBuilder) { 19 | this.aForm = this.fb.group({ 20 | name: ['', Validators.required], 21 | address: this.fb.group(new Address()), 22 | address: this.fb.group({ 23 | zip: ['', Validators.required] 24 | }) 25 | }); 26 | } 27 | } 28 | ``` 29 | 30 | ```html 31 |
32 | 33 |
34 | 35 |
36 |
37 | ``` 38 | 39 | 40 | ## 賦值給`FormGroup`項目 41 | 每個`FormGroup`的實體都一個兩個有關賦值的方法——`setValue({})`和`patchValue({})`(`FormControl`也有)。可以讓你隨需賦值給表單的項目,譬如注入變數有變化時,即時反應到表單內容,透過`ngOnChange`。 42 | 43 | - `setValue()`:會檢查傳入參數是否符合表單的資料結構,否則會提示錯誤,也就是傳入的物件必須完全符合表單的資料結構。 44 | - `patchValue()`:不會檢查表單的資料結構和提示錯誤,但可以隨意只針對某一項進行賦值。 45 | 46 | ```javascript 47 | export class AComponet { 48 | @Inject() hero: Hero; 49 | aForm: FormGroup; 50 | 51 | constructor() {} 52 | 53 | ngOnChange() { 54 | this.aForm.setValue({ 55 | name: this.hero.name, 56 | address: this.hero.addresses[0] || new Address() 57 | }) 58 | } 59 | } 60 | ``` 61 | 62 | ## 重設表單 63 | 有時候會想提供重設表單的選項給使用者,尤其當表單太過複雜時。而且整個表單重設可以取消所有項目的狀態(state),讓依賴(pristine或touch)的錯誤提示訊息可以回到最初狀態。一樣可以搭配`ngOnChange`時進行重設。 64 | 65 | ```javascript 66 | // 可以整個表單都重設 67 | this.aForm.reset(); 68 | 69 | // 或是針對某些項目進行重設 70 | this.aForm.reset({ 71 | name: this.hero.name, 72 | address: this.hero.addresses[0] || new Address() 73 | }) 74 | ``` 75 | 76 | 77 | ## 動態產生表單內容`FormArray` 78 | -------------------------------------------------------------------------------- /fundamentals/4-bootstraping.md: -------------------------------------------------------------------------------- 1 | # 啟動 2 | 3 | 導讀:https://angular.io/guide/bootstrapping 4 | 5 | Angular 是有一群 Angular 模組所組成的應用程式,最少會有一個根模組,慣例上會命名 6 | 為 `AppModule`。根據 CLI 所產生的根模組內容如下: 7 | 8 | ```ts 9 | import { BrowserModule } from '@angular/platform-browser'; 10 | import { NgModule } from '@angular/core'; 11 | 12 | import { AppComponent } from './app.component'; 13 | 14 | @NgModule({ 15 | declarations: [AppComponent], 16 | imports: [BrowserModule], 17 | providers: [], 18 | bootstrap: [AppComponent], 19 | }) 20 | export class AppModule {} 21 | ``` 22 | 23 | 模組是經由 `@NgModule` 修飾,裡面的項目包含三個所有模組都可使用的屬性 24 | (declarations, imports, providers; exports 在這裡使用是因為根模組通常不會匯出東 25 | 西),還有一個是根模組才會使用的屬性(bootstrap )。 26 | 27 | ### `declarations` 陣列 28 | 29 | 模組就好比一個容器,用於存放和管理內部的零件,Angular 的模組也類似。因此在模組內 30 | 的零件就必須在這裡進行「宣告」,讓容器的零件連結起來,互相知道彼此的存在。需要警 31 | 告宣告的東西分別是「component 、 directive、pipe 」。 32 | 33 | ### `providers` 陣列 34 | 35 | Angular 跟其他前端架構比較大的不同是, Angular 運用了相依性注入的方式來注入服務 36 | 。`providers` 陣列就是為了用來「註冊」服務的。 37 | 38 | ### `imports` 陣列 39 | 40 | 就如同 JavaScript 的模組一樣,Angular 的模組也可以透過匯入的方式來匯入其他 41 | Angular 模組。除了你自己寫的模組之外,Angular 的程式庫也是透過模組的方式進行開發 42 | ,譬如 `HttpClientModule`。匯入模組後,就可以在這個模組內使用它「註冊」的服務和 43 | 匯出的「component 、 directive、pipe 」 44 | 45 | ### `bootstrap` 陣列 46 | 47 | 應用程式在「啟動」根模組時會根據根模組內的(`bootstrap` 陣列)所指示的元件插入到 48 | 瀏覽器的 DOM 裡面。在 `bootstrap` 陣列內指派多個元件是合法的,但這並不是常見的做 49 | 法。 50 | 51 | ### 在 `main.ts` 中啟動 52 | 53 | 程式的啟動通常放在另一個檔案中,這是一個有 CLI 產生的 `main.ts`,負責啟動整個應 54 | 用程式。 55 | 56 | ```ts 57 | import { enableProdMode } from '@angular/core'; 58 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 59 | 60 | import { AppModule } from './app/app.module'; 61 | import { environment } from './environments/environment'; 62 | 63 | if (environment.production) { 64 | enableProdMode(); 65 | } 66 | 67 | platformBrowserDynamic().bootstrapModule(AppModule); 68 | ``` 69 | 70 | 這段程式碼建立一個瀏覽器平台用於動態編譯並啟動上述的 `AppModule`。啟動的過程包含 71 | 建立執行環境、從根模組中找到要被啟動的元件並建立元件的實體再插入到元件所定義的選 72 | 擇器名稱的元素標籤。 73 | 74 | 以 CLI 所產生的檔案為例,`AppModule` 的選擇器名稱定義為 `app-root` ,那程式啟動 75 | 時 Angular 就會找到 `index.html` 內的 ``,並做相應的動作。 76 | 77 | 當你的專案逐漸長大後,根據功能進行劃分,建立新的功能模組並考慮以延遲載入的方式設 78 | 定,將大幅改善應用程式的效能。 79 | -------------------------------------------------------------------------------- /fundamentals/How to start a new Angular project.md: -------------------------------------------------------------------------------- 1 | # 開發Angular專案的建議流程 2 | 來源:[如何在 Angular CLI 建立的 Angular 2 開發環境呼叫遠端 RESTful APIs](http://blog.miniasp.com/post/2017/02/05/Setup-proxy-to-backend-in-Angular-CLI.aspx) 3 | 4 | 1. 用@angular/cli建立一個新的專案,啟動它。 5 | ``` 6 | $ ng new project-name 7 | $ cd project-name 8 | $ npm start 或是 ng serve 9 | ``` 10 | 2. 設定Proxy Config,如果有必要向遠端伺服器或本機建立的`json-server`請求資源。 11 | - 在專案目錄下建立一個`proxy.config.json`檔案 12 | - `touch proxy.config.json` 13 | - 設定對應的請求資源url轉換,下例讓所有以`/api`開頭的請求都自動轉換成向`http://140.xxx.xxx.xxx/api`請求。 14 | ```json 15 | { 16 | "/api": { 17 | "target": "http://140.xxx.xxx.xxx", 18 | "secure": false 19 | } 20 | } 21 | ``` 22 | - 修改`package.json`裡面的`start`指令,讓程式以proxy的方式啟動更方便。 23 | ```json 24 | "scripts": { 25 | "start": "ng serve --proxy-config proxy.config.json" 26 | } 27 | ``` 28 | > **注意:**
雖然已使用代理的方式啟動,但你在瀏覽器console看到的訊息仍是向`http://localhost`請求,這並不是設定錯誤。 29 | 30 | 3. 設定虛擬的RESTful API伺服器。如果在本機端開發時就希望可以完全模擬好介接的遠端API伺服器。以下以`json-server`為例。 31 | - 全域安裝`json-server`。 32 | ``` 33 | $ npm install -g json-server 34 | ``` 35 | - 在專案目錄內或其他任意的地方設定好資料庫。 36 | - `touch db.json` 37 | ```json 38 | { 39 | "posts": [ 40 | { "id": 1, "title": "javascript" }, 41 | { "id": 1, "title": "typescript" }, 42 | { "id": 1, "title": "actionscript" } 43 | ], 44 | "profile": { "name": "typicode" } 45 | } 46 | ``` 47 | - 啟動`json-server`,預設埠號是3000,可以透過`--port`或`-p`自訂,避免衝突。還可以透過`--watch`即時觀察`db.json`有手動編輯檔案後,重新啟動伺服器。 48 | ``` 49 | $ json-server ./db.json --port 3200 --watch 50 | ``` 51 | - 自訂Routes。一般伺服器都以`/api`作為API的起始位置,若要最大程度的擬真,可以透過`json-server`的自訂Routes。 52 | - 在`db.json`相同目錄,或其他任意位置,建立一個routes.json設定檔。 53 | - `tounch routes.json` 54 | - 編輯`routes.json`,讓`/api`自動指向`/`。 55 | ```json 56 | { 57 | "/api": "/" 58 | } 59 | ``` 60 | - 啟動`json-server`。 61 | ``` 62 | $ json-server --watch ./db.json --routes ./routes.json --port 3200 63 | ``` 64 | - 嘗試請求`http://localhost:3200/api/posts`,應該就可以拿到正確資料。 65 | -------------------------------------------------------------------------------- /fundamentals/2-1-displaying-data.md: -------------------------------------------------------------------------------- 1 | # 顯示資料 2 | 導讀:https://angular.io/guide/displaying-data 3 | 4 | 跟傳統的靜態網頁開發不同,當代的架構(framework)都是以動態的方式進行開發和呈現, Angular 也不例外。所以學會如何把在元件中宣告的變數動態的顯示在模板上就是最基礎的馬步。 5 | 6 | ## 插補(interpolation) 7 | 插補是是模板語法中最基本的用法,用來在模板中插補動態的變數: 8 | 9 | ```js 10 | import { Component } from '@angular/core'; 11 | 12 | @Component({ 13 | selector: 'my-app', 14 | template: ` 15 | 16 |

{{title}}

17 |

My favorite hero is: {{myHero}}

18 | ` 19 | }) 20 | export class AppComponent { 21 | // 元件內宣告的變數 22 | title = 'Tour of Heroes'; 23 | myHero = 'Windstorm'; 24 | } 25 | ``` 26 | 27 | 插補內可填入的運算式(expression),這表示可以填入任何可以被「執行」式子: 28 | 29 | ```html 30 | 31 |

{{title + '2017'}}

32 | 33 |

My favorite hero is: {{myHero + (1 + 1)}}

34 | ``` 35 | 36 | > 最簡便的方式,請使用 `@angular/cli` 建立測試用專案 37 | > `ng new angular-test` 38 | 39 | 40 | ## `*ngFor` 顯示一連串的內容 41 | 插補的方式非常便利的在模板中顯示動態的內容,但如果要顯示的不僅僅是一個變數,而是一連串的大量資料呢?這時`*ngFor`就可以派上用,它的語法非常類似於 ES6 的 `for ( of )`。 42 | 43 | ```js 44 | import { Component } from '@angular/core'; 45 | 46 | @Component({ 47 | selector: 'my-app', 48 | template: ` 49 |

{{title}}

50 |

My favorite hero is: {{myHero}}

51 |

Heroes:

52 |
    53 | 54 |
  • 55 | 56 | {{ hero }} 57 |
  • 58 |
59 | ` 60 | }) 61 | export class AppComponent { 62 | title = 'Tour of Heroes'; 63 | // 要顯示的大量資料 64 | heroes = ['Windstorm', 'Bombasto', 'Magneta', 'Tornado']; 65 | myHero = this.heroes[0]; 66 | } 67 | ``` 68 | 69 | > ### 可迭(iterable) 70 | > `*ngFor`能夠處理的目標必須是任何具備可迭的變數。 71 | > 所以一般的物件是無法透過`*ngFor`來顯示的。 72 | 73 | 74 | ## `*ngIf` 選擇性顯示 75 | 學會單一變數的插補和大量資料的顯示後,再加上根據條件來選擇性顯示內容,那一套基礎的 Angular 模板組合拳就算學會了。 76 | 77 | `*ngIf`能夠接受布林值或最終可得到一個布林值的運算式(會套用 JavaScript 內建的自動轉型別機制),因此在使用上其實相當地靈活。 78 | 79 | ```html 80 | 81 |

There are many heroes!

82 | 83 |

There are many heroes!

84 | 85 |

There are many heroes!

86 | ``` 87 | 88 | 89 | ## 整理 90 | - `{{}}` 雙大括號用於單一變數的插補或顯示運算式結果。 91 | - `*ngFor` 顯示大量的資料,變數必須是可迭的。 92 | - `*ngIf` 根據條件選擇性顯示內容。 93 | -------------------------------------------------------------------------------- /fundamentals/RxJS in Angular.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## 基本的使用的RxJS 4 | ```js 5 | // 匯入要使用的RxJS的項目 6 | import { Observable } from 'rxjs/observable'; 7 | import 'rxjs/add/operator/catch'; 8 | import 'rxjs/add/operator/map'; 9 | 10 | getHeroes(): Observable { 11 | // 每個http的方法回傳的都是Observable物件 12 | return this.http.get(this.heroesUrl) 13 | .map(this.extractData) 14 | .catch(this.handleError); 15 | } 16 | 17 | // Response物件會自帶json()方法把body轉成JSON 18 | private extractData(res: Response) { 19 | let body = res.json(); 20 | return body.data || {}; 21 | } 22 | 23 | private handleError(error: Response | any) { 24 | let errMsg: string; 25 | 26 | if (error instanceof Response) { 27 | const body = error.json() || ''; 28 | const err = body.error || JSON.stringify(body); 29 | errMsg = `${error.status} - ${error.statusText || ''} ${err}`; 30 | } 31 | else { 32 | errMsg = error.message ? error.message : error.toString(); 33 | } 34 | 35 | // 實務上會回傳到伺服器做錯誤的紀錄 36 | console.log(errMsg); 37 | // 強制回傳一個Observable的錯誤,subscrible的時候,用第二個參數接錯誤 38 | return Observable.throw(errMsg); 39 | } 40 | ``` 41 | 42 | 43 | ### Observable vs. Promise 44 | 如果你原本就熟悉Promise的運作方式,你會發現兩者方式有點類似,只是在Promise時你期待的是用`then()`的方法去接,而Observable本身是一個**事件流**,所以每次會吐一個事件給你,這時就要用類似`map()`的方式去接(而實際上每一個HTTP請求也只會回傳一個事件回來而已,就那麼一個 XD)。 45 | 46 | 47 | 在捕捉錯誤的部分,方法一樣用`catch()`,只是Observable的`catch()`並非原生被JS/TS支援,所以要另外匯入`import 'rxjs/add/operator/catch'`(Map也一樣)。 48 | 49 | 50 | ## 回退到Promise的實作方式 51 | 雖然Observable可以帶來不少好處(但如果不會用、不想用就等於沒用),但是你還是可以把Observable重新轉成Promise物件來處理。以上述的`getHeroes()`為例: 52 | ```js 53 | import 'rxjs/add/topromise'; 54 | 55 | getHeroes(): Promise { 56 | return this.http.get(this.heroesUrl) 57 | .toPromise() 58 | .then(this.extractData) 59 | .catch(this.handleError); 60 | } 61 | 62 | private extractData(res: Response) { 63 | // 這裡拿到的還是Response物件,所以有json()方法 64 | let body = res.json(); 65 | return body.data || {}; 66 | } 67 | 68 | private handleError(error: Response | any) { 69 | let errMsg: string; 70 | if (error instanceof Response) { 71 | const body = error.json() || ''; 72 | const err = body.error || JSON.stringify(body); 73 | errMsg = `${error.status} - ${error.statusText || ''} ${err}`; 74 | } else { 75 | errMsg = error.message ? error.message : error.toString(); 76 | } 77 | console.error(errMsg); 78 | // 回傳一個Promise.reject強制丟出錯誤,後續一樣用catch()接。 79 | return Promise.reject(errMsg); 80 | } 81 | ``` 82 | -------------------------------------------------------------------------------- /learning.md: -------------------------------------------------------------------------------- 1 | # Learning resources 2 | 3 | ## Angular 4 | - [Angular Docs](https://angular.io/docs/ts/latest/) - Official 5 | - [Rangle's Angular Training Book](https://www.gitbook.com/book/rangle-io/ngcourse2/details) 6 | - [Angular 2 + 折騰記](http://blog.csdn.net/bomess/article/category/6769292) 7 | - [Angular 從 0 到 1](http://www.jianshu.com/p/2a12ddce544e) 8 | - [Angular 2 之 30 天邁向神乎其技之路](http://ithelp.ithome.com.tw/users/20103745/ironman/1160) 9 | - [Angular 4.x 修仙之路](https://segmentfault.com/blog/angular4) 10 | - [Angular 2 Fundamentals](http://courses.angularclass.com/p/angular-2-fundamentals) 11 | 12 | #### 開發環境 13 | - [Angular 4 開發環境說明](https://gist.github.com/doggy8088/15e434b43992cf25a78700438743774a) 14 | - [clang-format](https://forum.angular.tw/t/clang-format/235) 15 | 16 | #### Blog 17 | - [THOUGHTRAM](https://blog.thoughtram.io/all/) 18 | 19 | #### 效能檢查 20 | - [Angular Performance Checklist](https://github.com/mgechev/angular-performance-checklist) 21 | 22 | #### SSR 23 | - [Angular Server-Side Rendering with Angular CLI 1.3.x 基本設定](https://blog.kevinyang.net/2017/08/08/angular-cli-universal/) 24 | 25 | 26 | ## RxJS 27 | 28 | #### Book 29 | - [Learn RxJS](https://www.gitbook.com/book/btroncone/learn-rxjs/details) 30 | - [Online book for RxJS documention](https://xgrommx.github.io/rx-book/index.html) 31 | - [Multicasting operators in RxJS](http://blog.kwintenp.com/multicasting-operators-in-rxjs/#operator-overview) 32 | 33 | ### Video 34 | - [Observables and Reactive Programming in JS with RxJS](https://www.youtube.com/watch?v=Ah7Zu_lcd3o&list=PLZ66c9_z3umOuPSGsTu3mfzt6PGZeUyQZ) 35 | 36 | #### Series 37 | - [30 天精通RxJS](http://ithelp.ithome.com.tw/users/20103367/ironman/1199) 38 | 39 | #### Articles 40 | - [Angular 2 開發實戰:進階開發篇 - RxJS 新手入門](https://youtu.be/SDgX00fHos0) - Video 41 | - [RxJS: Observables, observers and operators introduction](https://toddmotto.com/rxjs-observables-observers-operators) 42 | - [RxJS Observable interop with Promises and Async-Await](https://medium.com/@benlesh/rxjs-observable-interop-with-promises-and-async-await-bebb05306875) 43 | - [What is RxJS? And Why You Should Know About It](https://news.thisdot.co/what-is-rxjs-and-why-you-should-know-about-it-2a5afe58cea) 44 | - [Observer vs Pub-Sub pattern](https://hackernoon.com/observer-vs-pub-sub-pattern-50d3b27f838c) 45 | 46 | #### Visual 47 | - [RxMarbles](http://rxmarbles.com/) 48 | 49 | 50 | ## Redux 51 | - [Angular Service Layers: Redux, RxJs and Ngrx Store - When to Use a Store And Why ?](http://blog.angular-university.io/angular-2-redux-ngrx-rxjs/) 52 | - [Tutorial: Building Redux in TypeScript with Angular 4 (Angular 2+)](http://blog.ng-book.com/introduction-to-redux-with-typescript-and-angular-2/) 53 | -------------------------------------------------------------------------------- /fundamentals/7.User Input.md: -------------------------------------------------------------------------------- 1 | ## 使用者輸入 2 | 導讀:[7. User Input](https://angular.io/docs/ts/latest/guide/user-input.html) 3 | 4 | 5 | 前面講過很多如何把已經有資料傳送到介面上去顯示,但是應用程式與使用者的互動往往不是單向的,有時也需要從使用者那裏拿到一些資料。這時單向繫結就已經滿足不了需求了,還需要借助事件繫結和雙向繫結。 6 | 7 | ### 怎麼知道使用者準備好了 8 | 使用者甚麼時候輸入好資料?甚麼時候決定要送出資料?我們需要利用一些 Angular 提供的機制來讓使用者作出通知的動作。雙向繫結就好比我們隨時觀察使用者輸入了甚麼,事件繫結就是準備好一些按鈕或鍵盤按鍵,當使用者作出相關的動作時,就表示他們準備好要把資料送出了。 9 | 10 | > 圍繞使用者輸入最常運用到的技術不外乎是就是 HTML 裡面的表單(Form)元素,譬如```