摘要:如果該構(gòu)造函數(shù)在我們所期望的中運(yùn)行,就沒有任何祖先注入器能夠提供的實(shí)例,于是注入器會(huì)放棄查找。但裝飾器表示找不到該服務(wù)也無(wú)所謂。用處理導(dǎo)航到子路由的情況。路由器會(huì)先按照從最深的子路由由下往上檢查的順序來(lái)檢查守護(hù)條件。
第一節(jié):Angular 2.0 從0到1 (一)
第二節(jié):Angular 2.0 從0到1 (二)
第三節(jié):Angular 2.0 從0到1 (三)
第四節(jié):Angular 2.0 從0到1 (四)
第五節(jié):Angular 2.0 從0到1 (五)
第四節(jié)我們完成的Todo的基本功能看起來(lái)還不錯(cuò),但是有個(gè)大問(wèn)題,就是每個(gè)用戶看到的都是一樣的待辦事項(xiàng),我們希望的是每個(gè)用戶擁有自己的待辦事項(xiàng)列表。我們來(lái)分析一下怎么做,如果每個(gè)todo對(duì)象帶一個(gè)UserId屬性是不是可以解決呢?好像可以,邏輯大概是這樣:用戶登錄后轉(zhuǎn)到/todo,TodoComponent得到當(dāng)前用戶的UserId,然后調(diào)用TodoService中的方法,傳入當(dāng)前用戶的UserId,TodoService中按UserId去篩選當(dāng)前用戶的Todos。
但可惜我們目前的LoginComponent還是個(gè)實(shí)驗(yàn)品,很多功能的缺失,我們是先去做Login呢,還是利用現(xiàn)有的Todo對(duì)象先試驗(yàn)一下呢?我個(gè)人的習(xí)慣是先進(jìn)行試驗(yàn)。
按之前我們分析的,給todo加一個(gè)userId屬性,我們手動(dòng)給我們目前的數(shù)據(jù)加上userId屬性吧。更改todo odo-data.json為下面的樣子:
{ "todos": [ { "id": "bf75769b-4810-64e9-d154-418ff2dbf55e", "desc": "getting up", "completed": false, "userId": 1 }, { "id": "5894a12f-dae1-5ab0-5761-1371ba4f703e", "desc": "have breakfast", "completed": true, "userId": 2 }, { "id": "0d2596c4-216b-df3d-1608-633899c5a549", "desc": "go to school", "completed": true, "userId": 1 }, { "id": "0b1f6614-1def-3346-f070-d6d39c02d6b7", "desc": "test", "completed": false, "userId": 2 }, { "id": "c1e02a43-6364-5515-1652-a772f0fab7b3", "desc": "This is a te", "completed": false, "userId": 1 } ] }
如果你還沒有啟動(dòng)json-server的話讓我們啟動(dòng)它: json-server ./src/app/todo/todo-data.json,然后打開瀏覽器在地址欄輸入http://localhost:3000/todos/?userId=2你會(huì)看到只有userId=2的json被輸出了
[ { "id": "5894a12f-dae1-5ab0-5761-1371ba4f703e", "desc": "have breakfast", "completed": true, "userId": 2 }, { "id": "0b1f6614-1def-3346-f070-d6d39c02d6b7", "desc": "test", "completed": false, "userId": 2 } ]
有興趣的話可以再試試http://localhost:3000/todos/?userId=2&completed=false或其他組合查詢?,F(xiàn)在todo有了userId字段,但我們還沒有User對(duì)象,User的json表現(xiàn)形式看起來(lái)應(yīng)該是這樣:
{ "id": 1, "username": "wang", "password": "1234" }
當(dāng)然這個(gè)表現(xiàn)形式有很多問(wèn)題,比如密碼是明文的,這些問(wèn)題我們先不管,但大概樣子是類似的。那么現(xiàn)在如果要建立User數(shù)據(jù)庫(kù)的話,我們應(yīng)該新建一個(gè)user-data.json
{ "users": [ { "id": 1, "username": "wang", "password": "1234" }, { "id": 2, "username": "peng", "password": "5678" } ] }
但這樣做的話感覺多帶帶為其建一個(gè)文件有點(diǎn)不值得,我們干脆把user和todo數(shù)據(jù)都放在一個(gè)文件吧,現(xiàn)在刪除./src/app/todo/todo-data.json刪除,在srcapp下面新建一個(gè)data.json
//srcappdata.json { "todos": [ { "id": "bf75769b-4810-64e9-d154-418ff2dbf55e", "desc": "getting up", "completed": false, "userId": 1 }, { "id": "5894a12f-dae1-5ab0-5761-1371ba4f703e", "desc": "have breakfast", "completed": true, "userId": 2 }, { "id": "0d2596c4-216b-df3d-1608-633899c5a549", "desc": "go to school", "completed": true, "userId": 1 }, { "id": "0b1f6614-1def-3346-f070-d6d39c02d6b7", "desc": "test", "completed": false, "userId": 2 }, { "id": "c1e02a43-6364-5515-1652-a772f0fab7b3", "desc": "This is a te", "completed": false, "userId": 1 } ], "users": [ { "id": 1, "username": "wang", "password": "1234" }, { "id": 2, "username": "peng", "password": "5678" } ] }
當(dāng)然有了數(shù)據(jù),我們就得有對(duì)應(yīng)的對(duì)象,基于同樣的理由,我們把所有的entity對(duì)象都放在一個(gè)文件:刪除srcapp odo odo.model.ts,在srcapp下新建一個(gè)目錄domain,然后在domain下新建一個(gè)entities.ts,請(qǐng)別忘了更新所有的引用。
export class Todo { id: string; desc: string; completed: boolean; userId: number; } export class User { id: number; username: string; password: string; }驗(yàn)證用戶賬戶的流程
我們來(lái)梳理一下用戶驗(yàn)證的流程
存儲(chǔ)要訪問(wèn)的URL
根據(jù)本地的已登錄標(biāo)識(shí)判斷是否此用戶已經(jīng)登錄,如果已登錄就直接放行
如果未登錄導(dǎo)航到登錄頁(yè)面 用戶填寫用戶名和密碼進(jìn)行登錄
系統(tǒng)根據(jù)用戶名查找用戶表中是否存在此用戶,如果不存在此用戶,返回錯(cuò)誤
如果存在對(duì)比填寫的密碼和存儲(chǔ)的密碼是否一致,如果不一致,返回錯(cuò)誤
如果一致,存儲(chǔ)此用戶的已登錄標(biāo)識(shí)到本地
導(dǎo)航到原本要訪問(wèn)的URL即第一步中存儲(chǔ)的URL,刪掉本地存儲(chǔ)的URL
看上去我們需要實(shí)現(xiàn)
UserService:用于通過(guò)用戶名查找用戶并返回用戶
AuthService:用于認(rèn)證用戶,其中需要利用UserService的方法
AuthGuard:路由攔截器,用于攔截到路由后通過(guò)AuthService來(lái)知道此用戶是否有權(quán)限訪問(wèn)該路由,根據(jù)結(jié)果導(dǎo)航到不同路徑。
看到這里,你可能有些疑問(wèn),為什么我們不把UserService和AuthService合并呢?這是因?yàn)閁serService是用于對(duì)用戶的操作的,不光認(rèn)證流程需要用到它,我們未來(lái)要實(shí)現(xiàn)的一系列功能都要用到它,比如注冊(cè)用戶,后臺(tái)用戶管理,以及主頁(yè)要顯示用戶名稱等。
根據(jù)這個(gè)邏輯流程,我們來(lái)組織一下代碼。開始之前我們想把認(rèn)證相關(guān)的代碼組織在一個(gè)新的模塊下,我們暫時(shí)叫它core吧。在srcapp下新建一個(gè)core目錄,然后在core下面新建一個(gè)core.module.ts
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from "@angular/core"; import { CommonModule } from "@angular/common"; @NgModule({ imports: [ CommonModule ] }) export class CoreModule { constructor (@Optional() @SkipSelf() parentModule: CoreModule) { if (parentModule) { throw new Error( "CoreModule is already loaded. Import it in the AppModule only"); } }
注意到這個(gè)模塊和其他模塊不太一樣,原因是我們希望只在應(yīng)用啟動(dòng)時(shí)導(dǎo)入它一次,而不會(huì)在其它地方導(dǎo)入它。在模塊的構(gòu)造函數(shù)中我們會(huì)要求Angular把CoreModule注入自身,這看起來(lái)像一個(gè)危險(xiǎn)的循環(huán)注入。不過(guò),@SkipSelf裝飾器意味著在當(dāng)前注入器的所有祖先注入器中尋找CoreModule。如果該構(gòu)造函數(shù)在我們所期望的AppModule中運(yùn)行,就沒有任何祖先注入器能夠提供CoreModule的實(shí)例,于是注入器會(huì)放棄查找。默認(rèn)情況下,當(dāng)注入器找不到想找的提供商時(shí),會(huì)拋出一個(gè)錯(cuò)誤。 但@Optional裝飾器表示找不到該服務(wù)也無(wú)所謂。 于是注入器會(huì)返回null,parentModule參數(shù)也就被賦成了空值,而構(gòu)造函數(shù)沒有任何異常。
那么我們?cè)谑裁磿r(shí)候會(huì)需要這樣一個(gè)模塊?比如在這個(gè)模塊中我們可能會(huì)要提供用戶服務(wù)(UserService),這樣的服務(wù)系統(tǒng)各個(gè)地方都需要,但我們不希望它被創(chuàng)建多次,希望它是一個(gè)單例。再比如某些只應(yīng)用于AppComponent模板的一次性組件,沒有必要共享它們,然而如果把它們留在根目錄,還是顯得太亂了。我們可以通過(guò)這種形式隱藏它們的實(shí)現(xiàn)細(xì)節(jié)。然后通過(guò)根模塊AppModule導(dǎo)入CoreModule來(lái)獲取其能力。
首先我們來(lái)看看Angular內(nèi)建的路由守衛(wèi)機(jī)制,在實(shí)際工作中我們常常會(huì)碰到下列需求:
該用戶可能無(wú)權(quán)導(dǎo)航到目標(biāo)組件。 導(dǎo)航前需要用戶先登錄(認(rèn)證)。
在顯示目標(biāo)組件前,我們可能得先獲取某些數(shù)據(jù)。
在離開組件前,我們可能要先保存修改。
我們可能要詢問(wèn)用戶:你是否要放棄本次更改,而不用保存它們?
我們可以往路由配置中添加守衛(wèi),來(lái)處理這些場(chǎng)景。守衛(wèi)返回true,導(dǎo)航過(guò)程會(huì)繼續(xù);返回false,導(dǎo)航過(guò)程會(huì)終止,且用戶會(huì)留在原地(守衛(wèi)還可以告訴路由器導(dǎo)航到別處,這樣也取消當(dāng)前的導(dǎo)航)。
路由器支持多種守衛(wèi):
用CanActivate來(lái)處理導(dǎo)航到某路由的情況。
用CanActivateChild處理導(dǎo)航到子路由的情況。
用CanDeactivate來(lái)處理從當(dāng)前路由離開的情況。
用Resolve在路由激活之前獲取路由數(shù)據(jù)。
用CanLoad來(lái)處理異步導(dǎo)航到某特性模塊的情況。
在分層路由的每個(gè)級(jí)別上,我們都可以設(shè)置多個(gè)守衛(wèi)。路由器會(huì)先按照從最深的子路由由下往上檢查的順序來(lái)檢查CanDeactivate守護(hù)條件。然后它會(huì)按照從上到下的順序檢查CanActivate守衛(wèi)。如果任何守衛(wèi)返回false,其它尚未完成的守衛(wèi)會(huì)被取消,這樣整個(gè)導(dǎo)航就被取消了。
本例中我們希望用戶未登錄前不能訪問(wèn)todo,那么需要使用CanActivate
import { AuthGuardService } from "../core/auth-guard.service"; const routes: Routes = [ { path: "todo/:filter", canActivate: [AuthGuardService], component: TodoComponent } ];
當(dāng)然光這么寫是沒有用的,下面我們來(lái)建立一個(gè)AuthGuardService,命令行中鍵入ng g s core/auth-guard(angular-cli對(duì)于Camel寫法的文件名是采用-來(lái)分隔每個(gè)大寫的詞)。
import { Injectable, Inject } from "@angular/core"; import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from "@angular/router"; @Injectable() export class AuthGuardService implements CanActivate { constructor(private router: Router) { } canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean { //取得用戶訪問(wèn)的URL let url: string = state.url; return this.checkLogin(url); } checkLogin(url: string): boolean { //如果用戶已經(jīng)登錄就放行 if (localStorage.getItem("userId") !== null) { return true; } //否則,存儲(chǔ)要訪問(wèn)的URl到本地 localStorage.setItem("redirectUrl", url); //然后導(dǎo)航到登陸頁(yè)面 this.router.navigate(["/login"]); //返回false,取消導(dǎo)航 return false; } }
觀察上面代碼,我們發(fā)現(xiàn)本地存儲(chǔ)的userId的存在與否決定了用戶是否已登錄的狀態(tài),這當(dāng)然是一個(gè)漏洞百出的實(shí)現(xiàn),但我們暫且不去管它?,F(xiàn)在我們要在登錄時(shí)把這個(gè)狀態(tài)值寫進(jìn)去。我們新建一個(gè)登錄鑒權(quán)的AuthService:ng g s core/auth
import { Injectable, Inject } from "@angular/core"; import { Http, Headers, Response } from "@angular/http"; import "rxjs/add/operator/toPromise"; import { Auth } from "../domain/entities"; @Injectable() export class AuthService { constructor(private http: Http, @Inject("user") private userService) { } loginWithCredentials(username: string, password: string): Promise{ return this.userService .findUser(username) .then(user => { let auth = new Auth(); localStorage.removeItem("userId"); let redirectUrl = (localStorage.getItem("redirectUrl") === null)? "/": localStorage.getItem("redirectUrl"); auth.redirectUrl = redirectUrl; if (null === user){ auth.hasError = true; auth.errMsg = "user not found"; } else if (password === user.password) { auth.user = Object.assign({}, user); auth.hasError = false; localStorage.setItem("userId",user.id); } else { auth.hasError = true; auth.errMsg = "password not match"; } return auth; }) .catch(this.handleError); } private handleError(error: any): Promise { console.error("An error occurred", error); // for demo purposes only return Promise.reject(error.message || error); } }
注意到我們返回了一個(gè)Auth對(duì)象,這是因?yàn)槲覀円缼准拢?/p>
用戶最初要導(dǎo)航的頁(yè)面URL
用戶對(duì)象
如果發(fā)生錯(cuò)誤的話,是什么錯(cuò)誤,我們需要反饋給用戶
這個(gè)Auth對(duì)象同樣在srcappdomainentities.ts中聲明
export class Auth { user: User; hasError: boolean; errMsg: string; redirectUrl: string; }
當(dāng)然我們還得實(shí)現(xiàn)UserService:ng g s user
import { Injectable } from "@angular/core"; import { Http, Headers, Response } from "@angular/http"; import "rxjs/add/operator/toPromise"; import { User } from "../domain/entities"; @Injectable() export class UserService { private api_url = "http://localhost:3000/users"; constructor(private http: Http) { } findUser(username: string): Promise{ const url = `${this.api_url}/?username=${username}`; return this.http.get(url) .toPromise() .then(res => { let users = res.json() as User[]; return (users.length>0)?users[0]:null; }) .catch(this.handleError); } private handleError(error: any): Promise { console.error("An error occurred", error); // for demo purposes only return Promise.reject(error.message || error); } }
這段代碼比較簡(jiǎn)單,就不細(xì)講了。下面我們改造一下srcapploginlogin.component.html,在原來(lái)用戶名的驗(yàn)證信息下加入,用于顯示用戶不存在或者密碼不對(duì)的情況
this is requiredshould be at least 3 charactors{{auth.errMsg}}
當(dāng)然我們還得改造srcapploginlogin.component.ts
import { Component, OnInit, Inject } from "@angular/core"; import { Router, ActivatedRoute, Params } from "@angular/router"; import { Auth } from "../domain/entities"; @Component({ selector: "app-login", templateUrl: "./login.component.html", styleUrls: ["./login.component.css"] }) export class LoginComponent implements OnInit { username = ""; password = ""; auth: Auth; constructor(@Inject("auth") private service, private router: Router) { } ngOnInit() { } onSubmit(formValue){ this.service .loginWithCredentials(formValue.login.username, formValue.login.password) .then(auth => { let redirectUrl = (auth.redirectUrl === null)? "/": auth.redirectUrl; if(!auth.hasError){ this.router.navigate([redirectUrl]); localStorage.removeItem("redirectUrl"); } else { this.auth = Object.assign({}, auth); } }); } }
然后我們別忘了在core模塊中聲明我們的服務(wù)srcappcorecore.module.ts
import { ModuleWithProviders, NgModule, Optional, SkipSelf } from "@angular/core"; import { CommonModule } from "@angular/common"; import { AuthService } from "./auth.service"; import { UserService } from "./user.service"; import { AuthGuardService } from "./auth-guard.service"; @NgModule({ imports: [ CommonModule ], providers: [ { provide: "auth", useClass: AuthService }, { provide: "user", useClass: UserService }, AuthGuardService ] }) export class CoreModule { constructor (@Optional() @SkipSelf() parentModule: CoreModule) { if (parentModule) { throw new Error( "CoreModule is already loaded. Import it in the AppModule only"); } } }
最后我們得改寫一下TodoService,因?yàn)槲覀冊(cè)L問(wèn)的URL變了,要傳遞的數(shù)據(jù)也有些變化
//todo.service.ts代碼片段 // POST /todos addTodo(desc:string): Promise{ //“+”是一個(gè)簡(jiǎn)易方法可以把string轉(zhuǎn)成number const userId:number = +localStorage.getItem("userId"); let todo = { id: UUID.UUID(), desc: desc, completed: false, userId }; return this.http .post(this.api_url, JSON.stringify(todo), {headers: this.headers}) .toPromise() .then(res => res.json() as Todo) .catch(this.handleError); } // GET /todos getTodos(): Promise { const userId = +localStorage.getItem("userId"); const url = `${this.api_url}/?userId=${userId}`; return this.http.get(url) .toPromise() .then(res => res.json() as Todo[]) .catch(this.handleError); } // GET /todos?completed=true/false filterTodos(filter: string): Promise { const userId:number = +localStorage.getItem("userId"); const url = `${this.api_url}/?userId=${userId}`; switch(filter){ case "ACTIVE": return this.http .get(`${url}&completed=false`) .toPromise() .then(res => res.json() as Todo[]) .catch(this.handleError); case "COMPLETED": return this.http .get(`${url}&completed=true`) .toPromise() .then(res => res.json() as Todo[]) .catch(this.handleError); default: return this.getTodos(); } }
現(xiàn)在應(yīng)該已經(jīng)ok了,我們來(lái)看看效果:
用戶密碼不匹配時(shí),顯示password not match
用戶不存在時(shí),顯示user not found
直接在瀏覽器地址欄輸入http://localhost:4200/todo,你會(huì)發(fā)現(xiàn)被重新導(dǎo)航到了login。輸入正確的用戶名密碼后,我們被導(dǎo)航到了todo,現(xiàn)在每個(gè)用戶都可以創(chuàng)建屬于自己的待辦事項(xiàng)了。
Angular團(tuán)隊(duì)推薦把路由模塊化,這樣便于使業(yè)務(wù)邏輯和路由松耦合。雖然目前在我們的應(yīng)用中感覺用處不大,但按官方推薦的方式還是和大家一起改造一下吧。刪掉原有的app.routes.ts和todo.routes.ts。添加app-routing.module.ts:
import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; import { LoginComponent } from "./login/login.component"; const routes: Routes = [ { path: "", redirectTo: "login", pathMatch: "full" }, { path: "login", component: LoginComponent }, { path: "todo", redirectTo: "todo/ALL" } ]; @NgModule({ imports: [ RouterModule.forRoot(routes) ], exports: [ RouterModule ] }) export class AppRoutingModule {}
以及srcapp odo odo-routing.module.ts
import { NgModule } from "@angular/core"; import { Routes, RouterModule } from "@angular/router"; import { TodoComponent } from "./todo.component"; import { AuthGuardService } from "../core/auth-guard.service"; const routes: Routes = [ { path: "todo/:filter", canActivate: [AuthGuardService], component: TodoComponent } ]; @NgModule({ imports: [ RouterModule.forChild(routes) ], exports: [ RouterModule ] }) export class TodoRoutingModule { }
并分別在AppModule和TodoModule中引入路由模塊。
用VSCode進(jìn)行調(diào)試有讀者問(wèn)如何用vscode進(jìn)行debug,這章我們來(lái)介紹一下。首先需要安裝一個(gè)vscode插件,點(diǎn)擊左側(cè)最下面的圖標(biāo)或者“在查看菜單中選擇命令面板,輸入install,選擇擴(kuò)展:安裝擴(kuò)展”,然后輸入“debugger for chrome”回車,點(diǎn)擊安裝即可。
然后點(diǎn)擊最左邊的倒數(shù)第二個(gè)按鈕
如果是第一次使用的話,齒輪圖標(biāo)上會(huì)有個(gè)紅點(diǎn),點(diǎn)擊選擇debugger for chrome,vscode會(huì)幫你創(chuàng)建一個(gè)配置文件,這個(gè)文件位于.vscodelaunch.json是debugger的配置文件,請(qǐng)改寫成下面的樣子。注意如果是MacOSX或者Linux,請(qǐng)把userDataDir替換成對(duì)應(yīng)的臨時(shí)目錄,另外把"webpack:///C:*":"C:/*"替換成"webpack:///*": "/*",這句是因?yàn)閍ngular-cli是采用webpack打包的,如果沒有使用angular-cli不需要添加這句。
{ "version": "0.2.0", "configurations": [ { "name": "Launch Chrome against localhost, with sourcemaps", "type": "chrome", "request": "launch", "url": "http://localhost:4200", "sourceMaps": true, "runtimeArgs": [ "--disable-session-crashed-bubble", "--disable-infobars" ], "diagnosticLogging": true, "webRoot": "${workspaceRoot}/src", //windows setup "userDataDir": "C: empchromeDummyDir", "sourceMapPathOverrides": { "webpack:///C:*":"C:/*" //use "webpack:///*": "/*" on Linux/OSX } }, { "name": "Attach to Chrome, with sourcemaps", "type": "chrome", "request": "attach", "port": 9222, "sourceMaps": true, "diagnosticLogging": true, "webRoot": "${workspaceRoot}/src", "sourceMapPathOverrides": { "webpack:///C:*":"C:/*" } } ] }
現(xiàn)在你可以試著在源碼中設(shè)置一個(gè)斷點(diǎn),點(diǎn)擊debug視圖中的debug按鈕,可以嘗試右鍵點(diǎn)擊變量把它放到監(jiān)視中看看變量值或者逐步調(diào)試應(yīng)用。
本章完整代碼見: https://github.com/wpcfan/awe...
第一節(jié):Angular 2.0 從0到1 (一)
第二節(jié):Angular 2.0 從0到1 (二)
第三節(jié):Angular 2.0 從0到1 (三)
第四節(jié):Angular 2.0 從0到1 (四)
第五節(jié):Angular 2.0 從0到1 (五)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/86823.html
摘要:官方支持微軟出品,是的超集,是的強(qiáng)類型版本作為首選編程語(yǔ)言,使得開發(fā)腳本語(yǔ)言的一些問(wèn)題可以更早更方便的找到。第一個(gè)組件那么我們來(lái)為我們的增加一個(gè)吧,在命令行窗口輸入。引導(dǎo)過(guò)程通過(guò)在中引導(dǎo)來(lái)啟動(dòng)應(yīng)用。它們的核心就是。 第一節(jié):Angular 2.0 從0到1 (一)第二節(jié):Angular 2.0 從0到1 (二)第三節(jié):Angular 2.0 從0到1 (三) 第一章:認(rèn)識(shí)Angular...
摘要:下面我們看看如果使用是什么樣子的,首先我們需要在組件的修飾器中配置,然后在組件的構(gòu)造函數(shù)中使用參數(shù)進(jìn)行依賴注入。 第一節(jié):Angular 2.0 從0到1 (一)第二節(jié):Angular 2.0 從0到1 (二)第三節(jié):Angular 2.0 從0到1 (三) 第二節(jié):用Form表單做一個(gè)登錄控件 對(duì)于login組件的小改造 在 hello-angularsrcapploginlogin...
摘要:如何在中使用動(dòng)畫前端掘金本文講一下中動(dòng)畫應(yīng)用的部分。與的快速入門指南推薦前端掘金是非常棒的框架,能夠創(chuàng)建功能強(qiáng)大,動(dòng)態(tài)功能的。自發(fā)布以來(lái),已經(jīng)廣泛應(yīng)用于開發(fā)中。 如何在 Angular 中使用動(dòng)畫 - 前端 - 掘金本文講一下Angular中動(dòng)畫應(yīng)用的部分。 首先,Angular本生不提供動(dòng)畫機(jī)制,需要在項(xiàng)目中加入Angular插件模塊ngAnimate才能完成Angular的動(dòng)畫機(jī)制...
摘要:而且此時(shí)我們注意到其實(shí)沒有任何一個(gè)地方目前還需引用了,這就是說(shuō)我們可以安全地把從組件中的修飾符中刪除了。 第一節(jié):Angular 2.0 從0到1 (一)第二節(jié):Angular 2.0 從0到1 (二)第三節(jié):Angular 2.0 從0到1 (三) 作者:王芃 wpcfan@gmail.com 第四節(jié):進(jìn)化!模塊化你的應(yīng)用 一個(gè)復(fù)雜組件的分拆 上一節(jié)的末尾我偷懶的甩出了大量代碼,可能...
摘要:接下來(lái)繼續(xù)介紹三種架構(gòu)模式,分別是查詢分離模式微服務(wù)模式多級(jí)緩存模式。分布式應(yīng)用程序可以基于實(shí)現(xiàn)諸如數(shù)據(jù)發(fā)布訂閱負(fù)載均衡命名服務(wù)分布式協(xié)調(diào)通知集群管理選舉分布式鎖和分布式隊(duì)列等功能。 SpringCloud 分布式配置 SpringCloud 分布式配置 史上最簡(jiǎn)單的 SpringCloud 教程 | 第九篇: 服務(wù)鏈路追蹤 (Spring Cloud Sleuth) 史上最簡(jiǎn)單的 S...
閱讀 2493·2021-09-30 09:47
閱讀 1440·2021-09-28 09:35
閱讀 3325·2021-09-22 15:57
閱讀 2585·2021-09-22 14:59
閱讀 3714·2021-09-07 10:25
閱讀 3152·2021-09-03 10:48
閱讀 3112·2021-08-26 14:14
閱讀 1020·2019-08-30 15:55