Angular WorkShop - 1 === ## Angular 9 開發環境說明 https://gist.github.com/doggy8088/15e434b43992cf25a78700438743774a ### 安裝 VSCODE https://code.visualstudio.com/ * 安裝 Angular Extension Pack 擴充套件 (Will) * 安裝 Prettier - Code formatter 擴充套件 ### 安裝 Node.js https://nodejs.org/ ``` node -v ``` ``` npm -v ``` ### 安裝 Angular CLI 工具 ``` npm install -g @angular/cli ``` ``` ng version ``` ``` Set-ExecutionPolicy RemoteSigned -Scope CurrentUser ``` ### 驗證 Angular CLI 執行 * 開啟「命令提示字元」視窗 (Windows) 或 Terminal 應用程式 (Mac OS X) * cd到自己想建立angular專案的資料夾 * 建立 demo1 專案資料夾與 Angular 專案骨架,請執行下列指令: ``` ng new demo1 ``` 這個過程會建立 Angular 專案檔案並自動安裝所有 npm 相依套件 * 進入 demo1 目錄 ```cd demo1``` * 啟動 Angular 開發伺服器 ``` ng serve ``` http://localhost:4200/ ![](https://i.imgur.com/C0xizJh.jpg) ## 認識 TypeScript 與 ES6 重要特性 * JavaScript 是實作 ECMASCript 規範的語言 * ES6 為了 因應大型或複雜應用程式的需求 而生 * TypeScript 是 Google 發展的,是 JavaScript 的超集合 ![](https://i.imgur.com/tKcLkTY.png) ### Angular 開發時會看到的語法 * 宣告變數的新關鍵字 let 和 const * 箭頭函式 * classes 的創建 * 引數的預設值 ### Declaration - let & const 解決 ```var``` hoisting(提升) 問題 > hoisting - 變數宣告會被隱含提升到其所在區域內的頂端 ![](https://i.imgur.com/iCWKeoi.png) 在執行時期時,所有var變數都會自動被hoisting。 因此若程式中有參考到未用var定義過的變數時, 會變成undefined,不會產生ERROR, 容易在DEBUG時造成誤解。 #### 用 let 取代 var ![](https://i.imgur.com/2LKe4hX.png) 使用let不會被hoisting,它是依附在{}的區塊中, 若程式中有參考到未用let定義過的變數時, 會產生ERROR,此行為比較接近常用的程式語言寫法。 #### 使用const定義常數值 不好的寫法 ![](https://i.imgur.com/gyCWn6p.png) 改用const會讓程式可容易閱讀 ![](https://i.imgur.com/zGrPjUD.png) const也不會有hoisting的問題 ### function #### arrow function arrow function ()=> 寫法如下 ```javascript= fnName(x,y) => { //... } ``` 傳統寫法與ES6寫法的比較 ```javascript= //ES5 function foo(x, y) { x++; y--; return x + y; } //ES6 foo(x, y) => { x++; y--; return x+y; } ``` ##### arrow function解決了this指向的問題 此範例中的this, 因為被settimeout callback function影響的關係, this變成指向window.this。 ```javascript= //沒有使用arrow function class Animal { constructor(){ this.type = 'animal' } says(say){ setTimeout(function(){ console.log(this.type + ' says ' + say) }, 1000) } } var animal = new Animal() animal.says('hi') //undefined says hi ``` 用了()=>之後,函數體內的this, 【就是定義時所在的對象】,而不是使用時所在的對象。 ()=>不會有自己的this,它的this是繼承外面的, 因此內部的this就是外層代碼區域的this。 ```javascript= class Animal { constructor(){ this.type = 'animal' } says(say){ setTimeout( () => { console.log(this.type + ' says ' + say) }, 1000) } } let animal = new Animal() animal.says('hi') //animal says hi ``` #### function defaults ```javascript= //ES5 function animal(type){ type = type || 'cat' console.log(type) } animal(); //ES6 animal => (type = 'cat'){ console.log(type) } animal(); ``` ### Objects, Strings, and Object.assign 既有型別或物件的擴充 #### 型別擴充 Object 更簡潔的屬性宣告。 ES6允許只寫屬性名稱, 屬性值為【屬性名稱所代表的變數值】 ```javascript= //ES5 function f1(x,y){ return {x:x,y:y}; } //ES6 function f2(x,y){ return {x,y}; } console.log(f1(1,2)); // Object {x:1,y:2} console.log(f2(1,2)); // Object {x:1,y:2} ``` #### 型別擴充 String 字串樣板(template literals):加入標籤自定義字串樣板 * 樣板起始符:`` (兩個反折號) * 不需要加上 \n 就可以完成字串的多行拼接 ```javascript= //ES5 var multilineText = ( "foo\n" + "bar\n" ); //ES6 var multilineText = ( `foo bar` ); ``` ${expression} 可在字串模版內,使用變數及函式 ![](https://i.imgur.com/IR97PpU.png) #### Object.assign 為了讓Function在各種不同應用程式可以重複使用, 會使用【options object】做function的參數傳入, 而不是用個別的具名參數。 ##### 使用Object.assign 做option object的初值及傳入值的合併。 ![](https://i.imgur.com/F5Vodj7.png) Object.assign若有指定多個Source Object, 且有相同的properties時,會讓"後"值蓋"前"值。 ![](https://i.imgur.com/vociddx.png) #### Arrays ES6提供更多操作Array的技巧 ##### Array Destructuring 可以將陣列或物件中的資料取出成獨立變數。 ![](https://i.imgur.com/fMr6eHn.png) ##### Combining Destructuring With Rest Params ![](https://i.imgur.com/z2dqj5M.png) ##### Using 【for…of】 to Loop Over Arrays ![](https://i.imgur.com/MjDJngI.png) ##### Array.find 回傳第一個滿足所提供之測試函式的元素值。 否則回傳 undefined。 ![](https://i.imgur.com/XjfeSlq.png) #### Classes 傳統的寫法 ![](https://i.imgur.com/IvcaBNK.png) 使用Class語法 ![](https://i.imgur.com/fRxgeJ9.png) ##### Class Inheritance 如同Java一樣使用extends來寫子類別 ![](https://i.imgur.com/rIBHOWu.png) 在子類別中覆寫方法 ![](https://i.imgur.com/NzOA76L.png) 在ES6之前,要做到JavaScript的OOP寫法, 必需要懂得如何使用prototype 但ES6之後,你可以像寫Java一樣寫JavaScript。 #### Modules 傳統的做法:所有Libary都被加到全域變數 * 容易造成全域變數汙染。 * 增加很多Side Effects,變數命名常常造成衝突。 ![](https://i.imgur.com/3YFQsJE.png) 使用模組來減少全域變數汙染的情形, * 利用export語法將variable或是function公開, * 利用import…from語法,引用module。 ![](https://i.imgur.com/SFCxEGX.png) ![](https://i.imgur.com/ZiRbIlm.jpg) ## 體驗 Angular 開發流程 終極目標:理解與完成 angular.tw 你的第一個應用 https://angular.tw/start ### Angular的應用程式組成 ![](https://i.imgur.com/Pe5OCGh.png) ### Angular元件的基本構成 ![](https://i.imgur.com/hUytrc6.png) ### 快速體驗一個 Angular 專案 #### App Component 根元件 app.component.ts ```typescript // 載入會用到的模組,angular/core 是 Angular 核心套件模組 import { Component } from '@angular/core'; @Component({ selector: 'app-root', // 根元件在html對應的tag名稱 templateUrl: './app.component.html', //根元件的html模版 styleUrls: ['./app.component.scss'] //根元件的css樣式 }) export class AppComponent { title = 'demo1'; } ``` #### 在index.html使用根元件 ```html <body> <app-root></app-root> </body> ``` #### 增加資料Binding ```html <span>{{ title }} app is {{action}}!</span> ``` ```typescript= export class AppComponent { title = 'everrich'; action = 'showing'; } ``` #### 建立子元件類別 * 在 terminal 輸入 angular cli 的語法就可以自動產生 ```ng generate component header``` ```ng g c header``` https://angular.tw/cli/generate ![](https://i.imgur.com/6NYOi6V.png) #### 在根元件中使用子元件 * 修改 app.component.ts的template 讓根元件認識要參考的子元件 ```html <app-header></app-header> <!-- Toolbar --> <div class="toolbar" role="banner"> ... </div> ``` ![](https://i.imgur.com/NPYAxRj.png) * 把 header 的 template html 從 根元件搬到子元件 ![](https://i.imgur.com/9NNJ7K9.png) ![](https://i.imgur.com/zP5OsTj.png) * 把 header 的 style 從 根元件搬到子元件 ![](https://i.imgur.com/Ru6fcAI.png) ![](https://i.imgur.com/egm9kO1.png) ### Data Binding 介紹 #### Data Binding - Interpolation ``` {{ expression }} ``` ![](https://i.imgur.com/Wv85RWJ.png) ![](https://i.imgur.com/oANt2ER.png) #### Data Binding - Property Binding ``` [property] = 變數 ``` ![](https://i.imgur.com/Pj2qLY3.png) #### Data Binding - Event Binding ``` (event) = your-custom-fn ``` ![](https://i.imgur.com/dtps8qy.png) ![](https://i.imgur.com/QdBFbd7.png) #### Data Binding - TwoWay Binding ``` [(ngModel)] = 變數 ``` * 使用 ngModel 前一定要先注意 是否有加入 FormsModule ``` // in app.module.ts import { FormsModule } from '@angular/forms'; ``` * 在 header.component.html 加上一個 input ```html <span (click)="alertGreeting()">{{headerGreeting}}</span> * <div class="spacer"></div> <!-- ↓↓↓ add input and bind ngModel here ↓↓↓ --> <input type="text" [(ngModel)]="headerGreeting"/> ``` ![](https://i.imgur.com/YUU4jPC.png) 此時修改 input 裡面的值,span 的 內容也會跟著變化 ### STRUCTURAL DIRECTIVES #### ngFor * 用來循環顯示Collection的資料 * 語法:```*ngFor="let item of collection"``` * 調整 app.component.html 拿掉 前置的ICON * 調整 app.component.ts 新增 resources 陣列變數 ![](https://i.imgur.com/svSrRnC.png) * 修改 app.component.html 使用 ngFor ```html <div class="card-container"> <a class="card" target="_blank" rel="noopener" [href]="r.link" *ngFor="let r of resources"> <span>{{r.title}}</span> <svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a> </div> ``` * 用 ```ng-container``` 幫我們整理 template 的語法 ```html <div class="card-container"> <ng-container *ngFor="let r of resources"> <a class="card" target="_blank" rel="noopener" [href]="r.link"> <span>{{r.title}}</span> <svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a> </ng-container> </div> ``` ##### ngFor use local variable ``` *ngFor="let idx = index ; let item of collection" ``` ![](https://i.imgur.com/VF27W89.png) * 修改 app.component.html 加上 index ``` <div class="card-container"> <ng-container *ngFor="let r of resources; let idx = index;"> <a class="card" target="_blank" rel="noopener" [href]="r.link"> <span>{{idx + 1}}.{{r.title}}</span> <svg class="material-icons" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24"><path d="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/></svg> </a> </ng-container> </div> ``` #### ngIf * 用來判斷是否顯示,語法 ```*ngIf="布林變數值"``` * 修改 app.component.html 使用 ngIf ``` <p *ngIf="idx === 0">Resource Start Here</p> ``` ## Q & A