`
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 |
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 |
3">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)元素,譬如```