-
[功能介紹-7] Structural Directives
Continue Reading…: [功能介紹-7] Structural Directives什麼是結構指令 結構指令負責HTML佈局。他們通常通過添加,刪除或操縱元素來塑造或重塑DOM的結構。結構指令很容易識別。通常會有星號(*)在指令屬性名稱前面。 以下是三種常件的結構指令的範例 一個attribute directive改變了元素的外觀或行為。例如,內置的NgStyle指令同時改變幾個元素樣式。 而Structural Directives則是會自動將裡面的內容儲存成一個ng-template並且操縱它,這也是為什麼這些指令前面會有一個星號(*)。 其實這個星號會悄悄的讓這個directive成為structure的directive 例如 其實等同於 我們可以觀察到 *號將該ngIf改為一個屬性綁定的元素,並綁定一個ng-template。*ngIf <ng-template> [ngIf] 剩下的部分<div>,包括它的class屬性,移到了<ng-template>元素之下。 我們可以將許多屬性指令寫在同一個host element上,但同一個host element只能夠有一個結構指令。 ng-template 從上面*所做的事情,我們可以知道,結構型指令是建立在ng-template之上的。 一個ng-template並不會一開始就顯示在畫面上,而是通過directive去操作裡面的dom並將要顯示的template添加在dom之中。 因此,*ngIf隱藏掉的物件,和我們使用css去show、hide在意義上是完全不同的。 因為它已經不在dom之上,是沒辦法被操作的。 不過由於若是網頁內的資料量大,angular有足夠的理由這樣做。 因為這可以避免過多的dom元素拖累網頁效能,若單純使用css去hide、show元素,所有的監聽器、物件依舊會在背景執行,這會讓效能變得不佳。 如果我們需要在show、hide物件的同時執行一些特殊的指令,可以用Lifecycle Hooks來撰寫此時要做的事情。 如何套用多個結構指令在元件裡 上面有提到,我們可以將許多屬性指令寫在同一個host element上,但同一個host element只能夠有一個結構指令。 所以在一般的狀態下,如果需要兩個標籤,我們會將HTML利用一些不會影響結構的標籤來做多層的Structural Directives控制 但有時候該狀況不允許任何多餘的標籤在裡面,例如下拉選單select。 若有一個區域選單select裡面的option內容要由*ngFor來產生,但是當city未選擇時,select又希望不要有任何下拉選單, 這時後我們在option上的確同時會需要放許多個結構指令,但ANGULAR不允許同一個標籤上放兩個結構指令。 如果我們多包一層span去包裡面的內容,會發現因為select內不允許span標籤,會造成讀不到option下拉選單,即便已經選擇了city 這時候就可以改用<ng-container> 就可以正常顯示了 自製一個結構Directive…
-
[功能介紹-6] Attribute Directives
Continue Reading…: [功能介紹-6] Attribute DirectivesDirective有分為結構型和屬性型的,這一篇要先介紹屬性型的Directive。 本篇的範例請見:Attribute Directive example / download example Angular有三種directive: 元件 – 包含template的directive。 結構指令 – 通過添加和刪除DOM元素來更改DOM佈局。(如ngFor、ngIf) 屬性指令 – 改變元素,組件或其他指令的外觀或行為。(如ngStyle) 建立一個簡單的屬性型directive 一個directive最少需要一個帶有@Directive宣告的檔案,告訴angular該如何識別這個directive。 這邊會用一個high light的directive範例來解說,當用戶將鼠標懸停在該元素上時,設置元素的背景色。你可以像這樣應用它: 而下面會一步步創立這一個directive。 首先,用cli創立一個新的directive 會看到cli自動建立了兩個新的檔案 產生的src/app/highlight.directive.ts檔案的內容如下: 當我們import了Directive後,就可以在檔案內使用@Directive來設定directive的CSS attribute selector 方括弧[]可以讓它成為一個屬性選擇器,在這邊為什麼會取為appHighlight而不是highlight,是為了避免與現有的html指令衝突,建議最好在所有的directive前面加上前綴字,以方便識別這是那邊來的屬性。如果沒有前綴字,很容易會發生難以識別的錯誤 接著看src/app/highlight.directive.ts的內容: 這邊可以看到在建構子的地方會傳入ElementRef,並且使用el.nativeElement直接修改DOM元素的背景顏色。 若希望directive能夠接收使用者事件,可以加載HostListener,如下: 顯示的結果如下圖: 當然,也可以通過標準的JavaScript來訪問DOM,並手動添加事件監聽器。這種方法至少有三個問題,是不被建議的: 需正確的堅聽到各種平台的事件 當要destroy這個directive時要手動移除事件 直接操作DOM API不是好的方式 輸入參數至directive 首先要先import所需的Input 然後宣告所要輸入的變數 那麼在使用directivet傳入參數的方式有下面幾種…
-
[功能介紹-5] 動態載入元件
Continue Reading…: [功能介紹-5] 動態載入元件模版組件有的時後會需要能夠動態被載入,在這邊會講要如何利用ComponentFactoryResolver來動態加入組件 本篇的例子是需要動態載入廣告橫幅,因為廣告內容會由幾個不同的團隊來打造,要在同一個區塊循環播放不同的廣告,因此較難把不同的廣告放在同一個component,這時後就會需要用到動態載入的功能 完整範例請見: live example / download example The anchor directive 在架構介紹的地方有介紹到,directive是一個沒有view的component,可以使用在html裡。 在這邊因為需要動態載入元件,會需要先建立一個directive,讓angular知道要把要動態載入的元件放在那邊。 ad.directive.ts這個檔案的內容如下: 首先我們將這個directive命名為[ad-host],然後注入ViewContainerRef,ViewContainerRef可以讓我們得知目前所在的HTML元素中包含的View內容,也可以透過它來改變View的結果(ex: 動態的產生Component、移除某個Component等等)。 Loading components 使用上面的directive的html碼如下 這邊可以看到我們使用ng-template來應用剛剛所創建的directive,ng-template指令表示一個Angular模板,這個標籤的內容將包含template的一部分,然後可以與其他template一起組成來成為最終的組件模板。使用ng-template標籤只是定義一個template,但是我們還沒有使用它。 Resolving components 下面是src/app/ad-banner.component.ts的內容 export class AdBannerComponent implements AfterViewInit, OnDestroy { @Input() ads: AdItem[];//要輪播顯示的廣告 currentAddIndex: number = -1; @ViewChild(AdDirective) adHost: AdDirective;//宣告剛剛建立的Directive,並藉由取得傳入的adHost subscription: any; interval:…
-
[功能介紹-4] 組件間的溝通
Continue Reading…: [功能介紹-4] 組件間的溝通通過輸入綁定數據從父項傳遞給子項 這是接受數據的子元件的內容hero-child.component.ts 子元件輸要被輸入的屬性有兩個,以@Input開頭來做宣告: 而這是父元件的內容,透過屬性綁定將值塞進子元件中 父元件的完整程式碼如下: 最後結果呈現: 攔截屬性的更改與設置 可將子元件的input宣告改為getter和setter,如下面範例 這樣當父元件傳空字串來時,子元件會自動輸出<no name set>字樣 Intercept input property changes with ngOnChanges() 在子組件裡,除了上述使用getter及setter外,也可以利用ngOnChanges()來取得改變的變數並做出回應 ngOnChanges()會傳入所有被改變的值changes: {[propKey: string]: SimpleChange} 下面是一個使用範例 子元件內容 父元件內容 最後成果如下圖 Parent listens for child event 子元件的EventEmitter財產是一個輸出的屬性,通常用@Output來宣告,如下: 而父元件的內容如下: 結果如圖: Parent interacts with child via local…
-
[功能介紹-3] Hooks的生命週期
Continue Reading…: [功能介紹-3] Hooks的生命週期Lifecycle Hooks 一個組件有一個由Angular管理的生命週期。 Angular創建、產生元件,當元件的數據綁定屬性改變時做檢查並確認,並在元件從DOM中刪除它之前destroys掉該元件。 Angular提供lifecycle hooks,可以讓我們在各個階段加上我們要讓元件做的事情。 directive具有相同的lifecycle hooks。 如何使用Lifecycle Hooks 下面為一個使用範例 Lifecycle sequence 鉤 目的和時機 ngOnChanges() Angular設置數據綁定的輸入屬性。該方法接收SimpleChanges當前和以前的屬性值的對象。 會在ngOnInit()之前被呼叫 ngOnInit() 在Angular之後初始化指令/組件首先顯示數據綁定屬性並設置指令/組件的輸入屬性。 ngDoCheck() 檢測Angular無法或無法自行檢測到的更改並採取相應措施。 ngAfterContentInit() 在Angular將外部內容設置到template之後被呼叫。 ngAfterContentChecked() 在Angular檢查投影到組件中的內容之後被呼叫。 ngAfterViewInit() 初始化組件的template和sub-template之後被呼叫。 ngAfterViewChecked() 在Angular檢查組件的視圖和子視圖之後作出響應。 ngOnDestroy 在Angular破壞指令/組件之前進行清理。取消訂閱Observables和事件處理程序以避免內存洩漏。 範例練習 範例網址:https://angular.io/guide/lifecycle-hooks#peek-a-boo
-
[功能介紹-2] 資料繫結的模版語法
Continue Reading…: [功能介紹-2] 資料繫結的模版語法Template Syntax 在上一篇的Angular架構中有提到,透過模版語法,template可以與component做許多的溝通。 那這一篇就會介紹Angular內建的模版語法參考。 對於tempalte.html來說,所有的html標籤都可以使用,除了<script>以外,這是為了維護模版的安全性,去除template被攻擊的風險,因此在template中所有的<script>會被忽略,並跳出警告。 接下來我們來介紹所有Angular的模板語法 {{…}} 以下為一個範例 大括號之間值通常是組件屬性的名稱。Angular使用相應組件屬性的字符串值替換該名稱。 在上面的例子中,Angular會取元件裡title和heroImageUrl的屬性,並會取代{{title}}及{{heroImageUrl}},因此在頁面上會顯示一個大的應用程序標題,然後一個英雄影像的位置。 在{{…}}之間,我們也可以使用模板表達式去轉換要顯示的值。 例如在刮弧中做運算: 或者也可以呼叫component的function getVal(): 大多的運算符都可以用在表達式裡面,除了一些會影響到component的值的運算符,如=、+=、-=之類。 有時候{{…}}裡面要綁定的數值也可以是在template定義的(使用#符號),請見下面的範例 「註」:如果在元件裡已經有變數名稱叫做hero,而template裡又有一個hero,在template會優先使用template內定義的變數。 在表達式中不能引用任何除了undefined外的全域變數,如window或document,也不能用consolo.log,只能使用元件內的屬性或方法,或者template裡上下文內的成員 在使用{{…}}時,有下面四個原則要注意: No visible side effects:不應該改變任何元件內的值,在rendering整個表式示時應該是穩定的 Quick execution:表達式的運算應該要很快,因為它會在許多狀況下被呼叫,因此若是裡面含有許多複雜運算時,請考慮用快取以增加效能。 Simplicity:雖然可以在{{…}}裡面寫很複雜的運算但是不建議,最多在裡面使用!符號,不然還是建議將運算放到元件內去計算,以利閱讀及開發 Idempotent: idempotent的意思是如果相同的操作再執行第二遍第三遍,結果還是跟第一遍的結果一樣 (也就是說不管執行幾次,結果都跟只有執行一次一樣)。 (event)=”statement” (event)=”statement”是Template statements。事件綁定是利用這樣的方式去做的,下面是一個範例: 與{{…}}不同的是,在這樣的語法中,是可以去改變元件的值的,並且被改變的值會透過單向綁定{{…}}顯示在畫面上。因此,(event)=”statement”的statement是能夠有支援=運算符的,但是+=、-=和++、–是不被允許的。 語句上下文也可以引用模板自己的上下文的屬性。在以下範例中,將模版$event對象,模板輸入變量(let hero)和模板引用變量(#heroForm)傳遞給組件的事件處理方法。 下面是一個範例 [target]=”statement” 以下是一個範例: 這樣的語法,能夠讓isUnchanged為true時,顯示這樣的畫面 綁定方法整理…
-
[功能介紹-1] Angular架構
Continue Reading…: [功能介紹-1] Angular架構Angular是一個框架,用來編寫html的應用程式,可以用javascript或typescript去編寫。 下圖是官網上所繪製的Angular架構圖: 首先先看最中間那一塊,是由template、metadata、component所構成的,這三個是一個component必備的元素。 所謂的元件可以看之前創建hero那篇文章,我們可以用下面指令創一個元件 創完一個元件後,可以看見下面這些檔案 Templates 裡面的component.html檔就是template,它看起來像是一個html的檔案,可以在裡面用資料綁定與事件綁定與controller裡的物件做繫結。 下面是一個template的範例: 可以注意到上面有些地方與一般的html不相同,例如像是*ngFor、{{hero.name}}、(click)=”selectHero(hero)”等… 這就是架構圖畫面上用來連繫Component以及Template的兩個箭頭,property binding以及event binding。 例如click事件繫結是(click)=’functionName()’,物件繫結可以用{{data}})。 透過這樣的繫結可以讓template將使用者操作的事件傳給component,component也可以將資料的更動即時的反饋到template所顯示的資料上。 下圖是非常清楚的binding類型列圖 其中{{hero.name}}為值繫結,可以綁定component裡的值。 [hero]=”selectedHero”為property binding,可以將某個元件裡的變數塞進一個HTML元素的屬性裡。。 (click)=”selectHero(hero)” event binding可以呼叫component裡的function 這個則是雙向數據綁定,在雙向綁定中,與屬性綁定一樣,數據屬性值將從組件輸入到輸入框中。用戶的更改也會返回到組件,將屬性重置為最新值,就像事件綁定一樣。 數據綁定在模板及其組件之間的通信中起著重要的作用。 數據綁定對於父組件和子組件之間的通信也很重要。 Component 而component.ts檔則是component內容,裡面會有一些屬性或方法來供template呼叫,下面是一個Component的簡單範例: 當用戶在應用程序中切換畫面時,Angular會創建,更新和銷毀組件。你的應用程序可以通過可選參加這個生命週期的每個時刻動作的lifecycle hooks,像ngOnInit()。 Metadata metadata則是在component.ts裡由@Component開頭的區塊來宣告,裡面會定義這個Component在要如何在別的元件的template裡被引用,templateUrl則是定義自己這個元件要顯示的html模版位置及styleUrls是css檔案位置。 例如下面這個metadata宣告 就可以用下面的方式來顯示這個元件 Modules 接著來介紹左上方的區塊 Angular應用程序是模組化的,Angular稱自己為NgModules的模組化系統。NgModules是一個很大的議題,在後面會有另一篇文章專門介紹ngModules。 一個小的application至少會有一個模組,稱為root module。雖然有些小的專案可能就只有一個模組,但大多大的專案都會有多個模組,稱為feature modules,一個模組內會是相同工作範疇的一組元件,它們有許多工作流程或功能上緊密相關,彼此協同運作。 當我們執行了這樣的指令來創建一個新的專案時 可以看到src/app資料夾內有app.module.ts這個檔案,這個檔案就是angular預設的根模組。…
-
[新手教程-7] 使用http來與API溝通
Continue Reading…: [新手教程-7] 使用http來與API溝通Angular處理http是使用rx(Reactive Programming)來實作的,類別的名稱為rxjs 在閱讀此篇之前,建議可以了解一下何謂Reactive Programming,其核心概念為何,這樣會比較容易理解本篇的內容 推薦閱讀:Reactive Programming 簡介與教學(以 RxJS 為例)、官網 ReactiveX RxJS教學:30 天精通 RxJS 使用http來取得api資料 將src/app/hero.service.ts取得資料的方式改由API取得 上面程式碼註解地方代表有修改過的地方。 所有的HttpClient方法都返回一個Observable的物件。一般來說,Observable物件會傳送多次資料給接收者,但http.get所取得的值為例外,因為http方法是發出要求並收到回應後就不會再有動作,因此它只會傳一次資料給取得這個資料的接收者。並且需要在被subscribe後才會啟動動作。 http.get預設接收的格式為json,會自動對照所取得的json轉化成一個對應屬性的物件以方便取得資料。 處理錯誤 如果在取得api時發生網路錯誤或其他問題導致無法順利取得伺服器資料時,可以用下面的方法來偵聽錯誤 首先,導入rxjs/operators類別 接著,使用pipe方法擴展Observable並在裡面下達catchError()來處理錯誤的狀況 下面則是catchError的內容 在上面的T是類型參數,在這個例子中,T代表Hero[]。這可以讓程式在打api失敗時依舊可取得符合應用程式所期望的類型的回傳值。 使用http修改伺服器資料 取得資料時使用get,而修改資料時使用put,其參數意義如下: 其中httpOptions較常設定的是headers,也就是宣告傳去的格式 下面是使用updateHero的範例 在RxJS中,有兩個角色,Observable和Subscription,Observable負責產生資料,創建後不會馬上啟動,而在關注(subscribe)後開始啟動。 使用http新增伺服器資料 新增資料在rxjs裡是使用http.post() 使用addHero方法的範例如下: 使用http刪除伺服器資料 刪除資料我們使用http.delete(),範例如下 而呼叫deleteHero的地方則要這樣寫: 上面我們可以看到在呼叫deleteHero時,即便刪除完成後沒有要多做任何時,仍然需要加上subscribe() 就像上面說過的,所有rxjs的動作都會在有人subscribe後才會呼叫,因此如果忽略subscribe(),http將不會將刪除請求發送到伺服器! Observable需要等到有東西subscribe它,才會被執行。 使用搜尋功能 於src/app/hero.service.ts增加搜索功能…
-
[新手教程-6] 使用Routing來切換頁面
Continue Reading…: [新手教程-6] 使用Routing來切換頁面什麼是Routing? Routing意指路由器,也就是由一個路由器來決定現在要顯示的頁面是什麼 在套用Routing時,會有下列的實踐流程 1. 套用轉址設定(讓伺服器不去真正網址所在的位置去讀取資料,而改由Routing來決定現在要顯示什麼畫面) 2. 由url分析要顯示的狀態是什麼 3. 由狀態去獲得真正要取得那些資訊 4. 從這些資訊組成實體 5. 套用導覽動作,由這個畫面切換至另一個畫面 在Angular裡,最佳做法是將載入和設定Routing放在一個top-level的模組內,並於AppModule內來import所有的路由資訊。 按照慣例,模組AppRoutingModule和app-routing.module.ts會放在src/app裡 Angular的Routing 產生的虛擬 URL,並不是真的存在於檔案系統裡,因此需要伺服器能夠支援,否則重新整理時會出現404 not found。 新增AppRoutingModule 透過下列CLI的指令來新增一個Routing –flat 的意思是將產出文件放在src/app裡,而非自己一個資料夾 –module=app 則是告知CLI註冊這個Routing在AppModule的imports裡 新產生的src/app/app-routing.module.ts檔案內容如下: 一般我們不會在Routing裡宣告元件,因此可以將@NgModule.declarations及CommonModule的宣告刪除 通常我們會使用Routes與RouterModule來實做Routing,修改後的檔案如下: 增加Routes 此時我們來設定不同路徑要導向的位置,如下面的程式碼 上述程式碼代表在網址為http://localhost/heroes時,會在標籤內顯示HeroesComponent這元件的內容。 另外,也要設定當伺服器剛運行時(http://localhost/),要顯示的模組。 設定完Router的map後,將之設定進RouterModule.forRoot()裡 然後再將這整個Router功能設定為可以被export,之後便可以在app.module裡import這個Router 而detail/:id裡的:id可於HeroDetailComponent裡去取得,來做進一步更詳細的資料顯示 取得方法如下: 我們可以發現,例子中不同的path為所傳入的參數不同,它們代表不同的意義,可以更多元化的設定Routing的功能。Routes可傳入的參數有下面這些 在這邊可看一下APP_BASE_HREF的設定方式 如果沒有設定,會跑出錯誤訊息如下:…
-
[新手教程-5] 建立Service
Continue Reading…: [新手教程-5] 建立Service這邊是上一篇Angular的主從元件開發的範例檔案:按此下載 創建 HeroService 使用Angular CLI創建一個名為hero的服務 我們會看到在src/app下多出了兩個檔案:hero.service.spec.ts和hero.service.ts 打開src/app/hero.service.ts可以看到下面內容 注意到我們用@Injectable() 來宣告HeroService這個類別,代表這個類別可能本身有依賴注入,對Service來說是強烈建議要加上這個宣告 將HeroService提供給app使用 Service可以供appModule、或任何Component使用,如果要給appModule使用, 則需在src/app/app.module.ts的@NgModule裡加入 這邊有更多NgModule的說明:NgModule 另外,若要使用CLI來自動完成providers的設定,則可以用下面的指令 修改HeroesComponent去使用HeroService裡的資料 打開src/app/heroes/heroes.component.ts,修改成以下的內容 讓getHeroes被更動時能夠被通知 Observable是RxJS library中很重要的一個功能。 當我們在src/app/hero.service.ts的getHeroes方法回傳值前面加上Observable宣告,當service內的資料變動時,其他注入的內容也會同步被異動 首先要先import下面兩個檔案 然後在getHeroes的回傳直類型前加上Observable宣告 注:若使用HttpClient.get<Hero[]>()亦會傳回Observable<Hero[]>,只是內容是由http傳來 在接收Observable物件時的方法也與非Observable物件的方式不同,過去若非Observable物件時,在hero.component.ts裡的getHeroes函數如下 若要使用Observable宣告則須改為: Observable.subscribe()是最關鍵的相異之處 最大的差異在於我們送出修改到伺服器傳回回應之間,未使用Observable.subscribe()方法時,它不會先凍結UI不讓使用者修改,而有可能會造成資料的不一致。而使用Observable.subscribe()可以避免這個問題。 本日範例下載: live example / download example
Search
About Me
17年資歷女工程師,專精於動畫、影像辨識以及即時串流程式開發。經常組織活動,邀請優秀的女性分享她們的技術專長,並在眾多場合分享自己的技術知識,也活躍於非營利組織,辦理活動來支持特殊兒及其家庭。期待用技術改變世界。
如果你認同我或想支持我的努力,歡迎請我喝一杯咖啡!讓我更有動力分享知識!