Angular產生的檔案實在太多辣,我們先專注在以下三個檔案:
app.component.css
app.component.html
app.component.ts
xxx.spec.ts
是寫測試時的檔案,學習階段時看到.spec.ts
都可以先忽略。
使用WebStorm IDE時,我個人會額外安裝以下這兩個套件
Component Folding 會讓同一個元件的四個檔案,以資料夾的形式(元件)來預覽
CLI QuickSwitch 則是按下alt+S
時,在同一個元件內切換檔案(.html
、.css
、.ts
)
什麼是範本、樣板(template)? 就是 .html
檔案
什麼是樣式(style)? 就是 .css
檔案
什麼是程式、元件(component)? 就是 .ts
檔案
有時後元件指的是以上三個(樣板、樣式、程式)檔案的集合體,
有時候是單純指.ts
檔案。
什麼是資料繫結(binding)
?
資料繫結就做一件事——綁定屬性值。
其中又分成單向繫結、雙向繫結
單向繫結僅能從component把值傳給template。
在綁定成功後,數值會傳遞過去、連動修改,
可以想像成是TS丟一個變數過去給HTML吃。
而雙向繫結則是兩邊都能連動、互傳資料。
以下四種方法前三種都是單向繫結,只有最後一項是雙向繫結。
{{property}}
把app.component.html
檔案內容修改為以下
HTML
<div class="container my-5">
<h1>{{title}}</h1>
<a href="{{url}}">{{link}}</a>
</div>
當看到兩個大括號{{}}
包住的值,
Angular會先對他們做解析(如果他不處理的話,HTML會看不懂這兩個大括號在這幹嘛用的)
並在app.component.ts
中設定屬性變數
TS
...
export class AppComponent {
title = 'My Website';
link = '前往Google';
url = 'https://google.com';
}
也可以在程式部分額外加上callback函式,
在兩秒後更換title文字,製造動態效果
TS
export class AppComponent {
title = 'My Website';
link = '前往Google';
url = 'https://google.com';
constructor() {
setTimeout(() => {
this.title = '我的網站';
}, 2000);
}
}
除了傳遞值,也可以在樣板中直接做運算
HTML
<p>a+b: {{a + b}}</p>
TS
...
export class AppComponent {
a = 10;
b = 50;
}
[property] = 'statement'
HTML
<div class="container my-5">
<h1>標題</h1>
<a [href]="url">Google連結</a>
</div>
這次不用兩個大括號了,當看到一個中括號[]
包住的屬性(property),Angular會先對他們做解析(如果他不處理的話HTML也會看不懂這個中括號是在幹嘛用的)
在HTML5裡面,我們可以透過
data-
前綴開頭的標籤來自由定義擴充attribute。
但自定義的data-title
沒辦法透過屬性繫結來做綁定,因為h1 DOM底下沒有這個property。不是所有有出現的attrubite都可以做屬性綁定要如何查h1有哪些property?
在chrome瀏覽器中進入開發者模式檢查
最右邊這一大串,這些property都可以進行binding
那除了property以外,要如何綁定自定義的attribute呢?
內嵌繫結
<h1 data-title={{title}}}>標題</h1>
也沒辦法使用解法:使用attr前綴來說明這是attribute屬性
[attr.data-title]=title
回頭來看,HTML確實吃到了自定義attribute
(event) = "method($event)"
(event) = "property=value"
此時加上一個button按鈕,將HTML稍微修改一下。
HTML
<div class="container my-5">
<input type="button" value="更換標題">
<h1 [attr.data-title]=title>標題</h1>
</div>
我希望點擊按鈕之後才會更換網頁大標題,可以透過Angular事件繫結做到
可以用(click)="changeTitle()"
來綁定事件
HTML
<div class="container my-5">
<input type="button" value="更換標題" (click)="changeTitle()">
<h1>{{title}}</h1>
</div>
同時也在typesrcipt中加一個方法(method)
TS
export class AppComponent {
title = 'My Website';
changeTitle(){
this.title = '更換後的標題';
}
}
也有另一種比較不常用的做法 on-click="method()"
概念同JS的onclick事件做到
HTML
<input type="button" value="更換標題" on-click="changeTitle()">
$event
透過event參數可以做到更多樣化的事
HTML
<div class="container my-5">
<input type="button" value="更換標題 (需同時按下Ctrl)" (click)="changeTitle($event)">
<h1>{{title}}</h1>
</div>
TS
export class AppComponent {
title = 'My Website';
changeTitle($event){
if ($event.ctrlKey){
this.title = '更換後的標題';
}
}
}
可標註傳入參數的型別
標註為MouseEvent事件型別
changeTitle($event: MouseEvent){
if ($event.ctrlKey){
this.title = '更換後的標題';
}
}
也可修改如下:
HTML
(click)="changeTitle($event.ctrlKey)
TS
changeTitle(ctrlKey: boolean){
if (ctrlKey){
this.title = '更換後的標題';
}
}
[(ngModel)] = 'property'
雙向繫結能同時做到屬性繫結()
以及事件繫結[]
,所以符號是[()]
用他來繫結某個property
HTML
<div class="container">
<input type="text" value="" placeholder="請輸入點什麼吧" [(ngModel)]="text">
<p>您的輸入: <span>{{text}}</span></p>
<p>字數: <span>{{text.length}}</span></p>
</div>
TS
export class AppComponent {
text = '';
}
回到瀏覽器上一看,就出現錯誤訊息了 Can't bind to 'ngModel' since it isn't a known property of 'input'.
看一下IDE,有出現一個提示Can't bind to [(ngModel)] since it is not provided by any applicable directives
回到 app.module.ts
中,import FomrsModule
就可以正常使用了
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { FormsModule } from '@angular/forms';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
FormsModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
看到這裡時就納悶了,為何使用到
ngModel
還要去import一個不相干的form模組
?
原因是 NgModelCreates a FormControl instance from a domain model and binds it to a form control element.
加上 按下ESC時清空輸入框的效果
HTML
<div class="container">
<input type="text" value="" placeholder="請輸入點什麼吧" [(ngModel)]="text" (keyup.escape)="textReset()">
<p>您的輸入: <span>{{text}}</span></p>
<p>字數: <span>{{text.length}}</span></p>
</div>
TS
export class AppComponent {
text = '';
textReset(){
this.text = '';
}
}
與前面幾種繫結方式不同。
預設情況下,範本參考變數只能在樣板中(.html
)使用
直接取得DOM物件,可以在當前的template中使用
變數名稱不要與template property衝突
<div class="container">
<input type="text" value="" placeholder="請輸入點什麼吧"
#tText
[(ngModel)]="text"
(keyup.escape)="textReset()">
<p>您的輸入: <span>{{text}}</span></p>
<p>字數: <span>{{tText.value.length}}</span></p>
</div>
Directive 就是
<app-...>
就是直接套在別的component元件上、存取該元件底下的所有property
建立一個新的元件
> ng g c header
在該元件中寫入一些HTML
<h1>Header!!</h1>
在app.component.html中加入該元件、引用進來
<div class="container">
<app-header #tHeader></app-header>
<input type="text" value="" placeholder="請輸入點什麼吧"
#tText
[(ngModel)]="text"
(keyup.escape)="textReset()">
<p>您的輸入: <span>{{text}}</span></p>
<p>字數: <span>{{tText.value.length}}</span></p>
<input type="button" value="更換標題" (click)="tHeader.title = '更改過後的標題'">
</div>