第一節:初識Angular-CLI
第二節:登陸組件的構建
第三節:創建一個待辦事項應用
第四節:進化!模塊化你的應用
第五節:多用戶版本的待辦事項應用
第六節:使用第三方樣式庫及模塊優化用
第七節:給組件帶來活力
Rx--隱藏在Angular 2.x中利劍
Redux你的Angular 2應用
第八節:查缺補漏大合集(上)
第九節:查缺補漏大合集(下)javascript
在 hello-angular\src\app\login\login.component.ts
中更改其模板爲下面的樣子css
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-login',
template: ` <div> <input type="text"> <button>Login</button> </div> `,
styles: []
})
export class LoginComponent implements OnInit {
constructor() { }
ngOnInit() {
}
}複製代碼
咱們增長了一個文本輸入框和一個按鈕,保存後返回瀏覽器能夠看到結果
html
<button (click)="onClick()">Login</button>
。
(click)
表示咱們要處理這個button的click事件,圓括號是說
發生此事件時,調用等號後面的表達式或函數。等號後面的
onClick()
是咱們本身定義在LoginComponent中的函數,這個名稱你能夠隨便定成什麼,不必定叫
onClick()
。下面咱們就來定義這個函數,在LoginComponent中寫一個叫
onClick()
的方法,內容很簡單就是把「button was clicked」輸出到Console。
onClick() {
console.log('button was clicked');
}複製代碼
返回瀏覽器,並按F12調出開發者工具。當你點擊Login時,會發現Console窗口輸出了咱們期待的文字。
java
usernameRef.value
,而後就能夠把
onClick()
方法改爲
onClick(usernameRef.value)
<div>
<input #usernameRef type="text"> <button (click)="onClick(usernameRef.value)">Login</button> </div>複製代碼
在Component內部的onClick方法也要隨之改寫成一個接受username的方法git
onClick(username) {
console.log(username);
}複製代碼
如今咱們再看看結果是什麼樣子,在文本輸入框中鍵入「hello」,點擊Login按鈕,觀察Console窗口:hello被輸出了。
github
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-login',
template: ` <div> <input #usernameRef type="text"> <input #passwordRef type="password"> <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button> </div> `,
styles: []
})
export class LoginComponent implements OnInit {
constructor() { }
ngOnInit() {
}
onClick(username, password) {
console.log('username:' + username + "\n\r" + "password:" + password);
}
}複製代碼
看看結果吧,在瀏覽器中第一個輸入框輸入「wang」,第二個輸入框輸入「1234567」,觀察Console窗口,Bingo!
json
若是咱們把登陸的業務邏輯在onClick方法中完成,固然也能夠,可是這樣作的耦合性太強了。設想一下,若是咱們增長了微信登陸、微博登陸等,業務邏輯會愈來愈複雜,顯然咱們須要把這個業務邏輯分離出去。那麼咱們接下來建立一個AuthService吧, 首先咱們在src\app下創建一個core的子文件夾(src\app\core
),而後命令行中輸入 ng g s core\auth
(s這裏是service的縮寫,core\auth是說在core的目錄下創建auth服務相關文件)。auth.service.ts
和auth.service.spec.ts
這個兩個文件應該已經出如今你的目錄裏了。數組
下面咱們爲這個service添加一個方法,你可能注意到這裏咱們爲這個方法指定了返回類型和參數類型。這就是TypeScript帶來的好處,有了類型約束,你在別處調用這個方法時,若是給出的參數類型或返回類型不正確,IDE就能夠直接告訴你錯了。瀏覽器
import { Injectable } from '@angular/core';
@Injectable()
export class AuthService {
constructor() { }
loginWithCredentials(username: string, password: string): boolean {
if(username === 'wangpeng')
return true;
return false;
}
}複製代碼
等一下,這個service雖然被建立了,但仍然沒法在Component中使用。固然你能夠在Component中import這個服務,而後實例化後使用,可是這樣作並很差,仍然時一個緊耦合的模式,Angular2提供了一種依賴性注入(Dependency Injection)的方法。微信
若是不使用DI(依賴性注入)的時候,咱們天然的想法是這樣的,在login.component.ts
中import引入AuthService,在構造中初始化service,在onClick中調用service。
import { Component, OnInit } from '@angular/core';
//引入AuthService
import { AuthService } from '../core/auth.service';
@Component({
selector: 'app-login',
template: ` <div> <input #usernameRef type="text"> <input #passwordRef type="password"> <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button> </div> `,
styles: []
})
export class LoginComponent implements OnInit {
//聲明成員變量,其類型爲AuthService
service: AuthService;
constructor() {
this.service = new AuthService();
}
ngOnInit() {
}
onClick(username, password) {
//調用service的方法
console.log('auth result is: ' + this.service.loginWithCredentials(username, password));
}
}複製代碼
這麼作呢也能夠跑起來,但存在幾個問題:
下面咱們看看若是使用DI是什麼樣子的,首先咱們須要在組件的修飾器中配置AuthService,而後在組件的構造函數中使用參數進行依賴注入。
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../core/auth.service';
@Component({
selector: 'app-login',
template: ` <div> <input #usernameRef type="text"> <input #passwordRef type="password"> <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button> </div> `,
styles: [],
//在providers中配置AuthService
providers:[AuthService]
})
export class LoginComponent implements OnInit {
//在構造函數中將AuthService示例注入到成員變量service中
//並且咱們不須要顯式聲明成員變量service了
constructor(private service: AuthService) {
}
ngOnInit() {
}
onClick(username, password) {
console.log('auth result is: ' + this.service.loginWithCredentials(username, password));
}
}複製代碼
看到這裏你會發現咱們仍然須要import相關的服務,這是import是要將類型引入進來,而provider裏面會配置這個類型的實例。固然即便這樣仍是不太爽,可不能夠不引入AuthService呢?答案是能夠。
咱們看一下app.module.ts
,這個根模塊文件中咱們發現也有個providers,根模塊中的這個providers是配置在模塊中全局可用的service或參數的。
providers: [
{provide: 'auth', useClass: AuthService}
]複製代碼
providers是一個數組,這個數組呢實際上是把你想要注入到其餘組件中的服務配置在這裏。你們注意到咱們這裏的寫法和上面優勢區別,沒有直接寫成
providers:[AuthService]複製代碼
而是給出了一個對象,裏面有兩個屬性,provide和useClass,provide定義了這個服務的名稱,有須要注入這個服務的就引用這個名稱就好。useClass指明這個名稱對應的服務是一個類,本例中就是AuthService了。這樣定義好以後,咱們就能夠在任意組件中注入這個依賴了。下面咱們改動一下login.component.ts
,去掉頭部的import { AuthService } from '../core/auth.service';
和組件修飾器中的providers,更改其構造函數爲
onstructor(@Inject('auth') private service) {
}複製代碼
咱們去掉了service的類型聲明,但加了一個修飾符@Inject('auth')
,這個修飾符的意思是請到系統配置中找到名稱爲auth
的那個依賴注入到我修飾的變量中。固然這樣改完後你會發現Inject
這個修飾符系統不識別,咱們須要在@angular/core
中引用這個修飾符,如今login.component.ts
看起來應該是下面這個樣子
import { Component, OnInit, Inject } from '@angular/core';
@Component({
selector: 'app-login',
template: ` <div> <input #usernameRef type="text"> <input #passwordRef type="password"> <button (click)="onClick(usernameRef.value, passwordRef.value)">Login</button> </div> `,
styles: []
})
export class LoginComponent implements OnInit {
constructor(@Inject('auth') private service) {
}
ngOnInit() {
}
onClick(username, password) {
console.log('auth result is: ' + this.service.loginWithCredentials(username, password));
}
}複製代碼
接下來的問題是咱們是否只能經過這種方式進行表現層和邏輯之間的數據交換呢?若是咱們但願在組件內對數據進行操做後再反饋到界面怎麼處理呢?Angular2提供了一個雙向數據綁定的機制。這個機制是這樣的,在組件中提供成員數據變量,而後在模板中引用這個數據變量。咱們來改造一下login.component.ts
,首先在class中聲明2個數據變量username和password。
username = "";
password = "";複製代碼
而後去掉onClick
方法的參數,並將內部的語句改形成以下樣子:
console.log('auth result is: '
+ this.service.loginWithCredentials(this.username, this.password));複製代碼
去掉參數的緣由是雙向綁定後,咱們經過數據成員變量就能夠知道用戶名和密碼了,不須要在傳遞參數了。而成員變量的引用方式是this.成員變量
。
而後咱們來改造模板:
<div>
<input type="text" [(ngModel)]="username" />
<input type="password" [(ngModel)]="password" />
<button (click)="onClick()">Login</button>
</div>複製代碼
[(ngModel)]="username"
這個看起來很彆扭,稍微解釋一下,方括號[]的做用是說把等號後面當成表達式來解析而不是當成字符串,若是咱們去掉方括號那就等於說是直接給這個ngModel賦值成「username」這個字符串了。方括號的含義是單向綁定,就是說咱們在組件中給model賦的值會設置到HTML的input控件中。[()]
是雙向綁定的意思,就是說HTML對應控件的狀態的改變會反射設置到組件的model中。ngModel是FormModule中提供的指令,它負責從Domain Model(這裏就是username或password,之後咱們可用綁定更復雜的對象)中建立一個FormControl的實例,並將這個實例和表單的控件綁定起來。一樣的對於click事件的處理,咱們不須要傳入參數了,由於其調用的是剛剛咱們改造的組件中的onClick方法。如今咱們保存文件後打開瀏覽器看一下,效果和上一節的應該同樣的。本節的完整代碼以下:
//login.component.ts
import { Component, OnInit, Inject } from '@angular/core';
@Component({
selector: 'app-login',
template: ` <div> <input type="text" [(ngModel)]="username" /> <input type="password" [(ngModel)]="password" /> <button (click)="onClick()">Login</button> </div> `,
styles: []
})
export class LoginComponent implements OnInit {
username = '';
password = '';
constructor(@Inject('auth') private service) {
}
ngOnInit() {
}
onClick() {
console.log('auth result is: '
+ this.service.loginWithCredentials(this.username, this.password));
}
}複製代碼
一般狀況下,表單的數據是有必定的規則的,咱們須要依照其規則對輸入的數據作驗證以及反饋驗證結果。Angular2中對錶單驗證有很是完善的支持,咱們繼續上面的例子,在login
組件中,咱們定義了一個用戶名和密碼的輸入框,如今咱們來爲它們加上規則。首先咱們定義一下規則,用戶名和密碼都是必須輸入的,也就是不能爲空。更改login.component.ts
中的模板爲下面的樣子
<div>
<input required type="text" [(ngModel)]="username" #usernameRef="ngModel" />
{{usernameRef.valid}}
<input required type="password" [(ngModel)]="password" #passwordRef="ngModel" />
{{passwordRef.valid}}
<button (click)="onClick()">Login</button>
</div>複製代碼
注意到咱們只是爲username和password兩個控件加上了required這個屬性,代表這兩個控件爲必填項。經過#usernameRef="ngModel"
咱們從新又加入了引用,此次的引用指向了ngModel,這個引用是要在模板中使用的,因此才加入這個引用若是不須要在模板中使用,能夠不要這句。{{表達式}}
雙花括號表示解析括號中的表達式,並把這個值輸出到模板中。這裏咱們爲了能夠顯性的看到控件的驗證狀態,直接在對應控件後輸出了驗證的狀態。初始狀態能夠看到2個控件的驗證狀態都是false,試着填寫一些字符在兩個輸入框中,看看狀態變化吧。
咱們是知道了驗證的狀態是什麼,可是若是咱們想知道驗證失敗的緣由怎麼辦呢?咱們只須要將{{usernameRef.valid}}
替換成{{usernameRef.errors | json}}
。|
是管道操做符,用於將前面的結果經過管道輸出成另外一種格式,這裏就是把errors對象輸出成json格式的意思。看一下結果吧,返回的結果以下
<input type="text" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" />複製代碼
{{表達式}}
替換成友好的錯誤提示,咱們想在有錯誤發生時顯示錯誤的提示信息。那麼咱們來改造一下template。
<div>
<input type="text" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" />
{{ usernameRef.errors | json }}
<div *ngIf="usernameRef.errors?.required">this is required</div>
<div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div>
<input required type="password" [(ngModel)]="password" #passwordRef="ngModel" />
<div *ngIf="passwordRef.errors?.required">this is required</div>
<button (click)="onClick()">Login</button>
</div>複製代碼
ngIf
也是一個Angular2的指令,顧名思義,是用於作條件判斷的。*ngIf="usernameRef.errors?.required"
的意思是當usernameRef.errors.required
爲true
時顯示div
標籤。那麼那個?
是幹嗎的呢?由於errors
多是個null,若是這個時候調用errors
的required
屬性確定會引起異常,那麼?
就是標明errors
可能爲空,在其爲空時就不用調用後面的屬性了。
若是咱們把用戶名和密碼整個當作一個表單的話,咱們應該把它們放在一對<form></form>
標籤中,相似的加入一個表單的引用formRef
。
<div>
<form #formRef="ngForm">
<input type="text" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" />
<div *ngIf="usernameRef.errors?.required">this is required</div>
<div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div>
<input type="password" [(ngModel)]="password" #passwordRef="ngModel" required />
<div *ngIf="passwordRef.errors?.required">this is required</div>
<button (click)="onClick()">Login</button>
</form>
</div>複製代碼
這時運行後會發現本來好用的代碼出錯了,這是因爲若是在一個大的表單中,ngModel會註冊成Form的一個子控件,註冊子控件須要一個name,這要求咱們顯式的指定對應控件的name,所以咱們須要爲input
增長name屬性
<div>
<form #formRef="ngForm">
<input type="text" name="username" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" />
<div *ngIf="usernameRef.errors?.required">this is required</div>
<div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div>
<input type="password" name="password" [(ngModel)]="password" #passwordRef="ngModel" required />
<div *ngIf="passwordRef.errors?.required">this is required</div>
<button (click)="onClick()">Login</button>
<button type="submit">Submit</button>
</form>
</div>複製代碼
既然咱們增長了一個formRef
,咱們就看看formRef.value
有什麼吧。
首先爲form增長一個表單提交事件的處理<form #formRef="ngForm" (ngSubmit)="onSubmit(formRef.value)">
。
而後在組件中增長一個onSubmit
方法
onSubmit(formValue) {
console.log(formValue);
}複製代碼
你會發現formRef.value
中包括了表單全部填寫項的值。
fieldset
標籤用來處理。那麼咱們看看怎麼和Angular2結合吧:
<div>
<form #formRef="ngForm" (ngSubmit)="onSubmit(formRef.value)">
<fieldset ngModelGroup="login">
<input type="text" name="username" [(ngModel)]="username" #usernameRef="ngModel" required minlength="3" />
<div *ngIf="usernameRef.errors?.required">this is required</div>
<div *ngIf="usernameRef.errors?.minlength">should be at least 3 charactors</div>
<input type="password" name="password" [(ngModel)]="password" #passwordRef="ngModel" required />
<div *ngIf="passwordRef.errors?.required">this is required</div>
<button (click)="onClick()">Login</button>
<button type="submit">Submit</button>
</fieldset>
</form>
</div>複製代碼
<fieldset ngModelGroup="login">
意味着咱們對於fieldset以內的數據都分組到了login
對象中。
<button (click)="onClick()">Login</button>
,而後把
<button type="submit">
標籤後的
Submit
文本替換成
Login
,最後改寫onSubmit方法。
onSubmit(formValue) {
console.log('auth result is: '
+ this.service.loginWithCredentials(formValue.login.username, formValue.login.password));
}複製代碼
在瀏覽器中試驗一下吧,全部功能正常工做。
若是咱們在開發工具中查看網頁源碼,能夠看到
ng-invalid
<input name="username" class="ng-pristine ng-invalid ng-touched" required="" type="text" minlength="3" ng-reflect-minlength="3" ng-reflect-name="username">複製代碼
相似的能夠實驗一下,填入一些字符知足驗證要求以後,看input的HTML是下面的樣子:在驗證結果爲true時input的樣式是ng-valid
<input name="username" class="ng-touched ng-dirty ng-valid" required="" type="text" ng-reflect-model="ssdsds" minlength="3" ng-reflect-minlength="3" ng-reflect-name="username">複製代碼
知道這個後,咱們能夠自定義不一樣驗證狀態下的控件樣式。在組件的修飾符中把styles數組改寫一下:
styles: [` .ng-invalid{ border: 3px solid red; } .ng-valid{ border: 3px solid green; } `]複製代碼
保存一下,返回瀏覽器能夠看到,驗證不經過時
最後說一下,咱們看到這樣設置完樣式後連form和fieldset都一塊兒設置了,這是因爲form和fieldset也在樣式中應用了.ng-valid
和.ng-valid
,那怎麼解決呢?只須要在.ng-valid
加上input
便可,它代表的是應用於input類型控件而且class引用了ng-invalid的元素。
styles: [` input.ng-invalid{ border: 3px solid red; } input.ng-valid{ border: 3px solid green; } `]複製代碼
不少開發人員不太瞭解CSS,其實CSS仍是比較簡單的,我建議先從Selector開始看,Selector的概念弄懂後Angular2的開發CSS就會順暢不少。具體可見W3School中對於CSS Selctor的參考和css-tricks.com/multiple-cl…
本節代碼: github.com/wpcfan/awes…
紙書出版了,比網上內容豐富充實了,歡迎你們訂購!
京東連接:item.m.jd.com/product/120…
第一節:Angular 2.0 從0到1 (一)
第二節:Angular 2.0 從0到1 (二)
第三節:Angular 2.0 從0到1 (三)
第四節:Angular 2.0 從0到1 (四)
第五節:Angular 2.0 從0到1 (五)
第六節:Angular 2.0 從0到1 (六)
第七節:Angular 2.0 從0到1 (七)
第八節:Angular 2.0 從0到1 (八)