入坑
前端開(kāi)發(fā)者估計(jì)跟我的心思是一樣的虚倒,剛對(duì)AngularJS寫(xiě)出了點(diǎn)頭緒美侦,懂了點(diǎn)皮毛,轉(zhuǎn)眼升級(jí)版Angular4.x就出現(xiàn)了魂奥,心中是各種禮貌的問(wèn)候的菠剩。。耻煤。
默默擦眼淚具壮。。哈蝇。我們來(lái)看下要從哪幾個(gè)方面入手棺妓。以下目錄~
- 路由
- 指令
- 組件
- 模塊
- 服務(wù)
Angular拋棄了原本的 ng-controller指令、復(fù)雜的$scope炮赦,保留了路由嵌套怜跑、依賴(lài)注入機(jī)制。還有一些指令的寫(xiě)法吠勘,例如ng-click 改成(click) 性芬、ng-repeat改成 *ngFor等等,后續(xù)指令篇會(huì)詳細(xì)提出看幼。Angular的最大改造是用Typescript為默認(rèn)開(kāi)發(fā)語(yǔ)言批旺,組件化的思維。
入坑前需要了解一下Angular-Cli工具诵姜。在項(xiàng)目初始化的時(shí)候,可以開(kāi)箱即用,搭配一系列完整的工具
npm install @angular/cli -g
ng new study-ng
cd study-ng
ng serve
打開(kāi)package.json文件
"@angular/animations": "^5.2.0",
"@angular/common": "^5.2.0",
"@angular/compiler": "^5.2.0",
"@angular/core": "^5.2.0",
"@angular/forms": "^5.2.0",
"@angular/http": "^5.2.0",
"@angular/platform-browser": "^5.2.0",
"@angular/platform-browser-dynamic": "^5.2.0",
"@angular/router": "^5.2.0",
- @angular/common:
- CommonModule:通用模塊棚唆,包含內(nèi)置指令ngIf暇赤,ngFor
- @angular/core:包含多種常用的模塊
- NgModule(模塊定義裝飾器)
- Component(組件定義裝飾器)
- Directive (指令定義裝飾器)
- ElemtRef (元素引用)
- ViewChild (獲取子元素)
- Output Input EventEmitter Render 等等等。宵凌。鞋囊。。
- @angular/forms:
- Validators:表單校驗(yàn)
- @angular/http:
- HttpModule:http請(qǐng)求模塊
- @angular/router:
- RouterModule:路由模塊
天啊嚕O贡埂!太多了挺益。~~ps:這些都是擼過(guò)代碼后從頭看理論概念的時(shí)候能理解透的望众。伞辛。蚤氏。硬生生的啃文字竿滨,小火雞我是受不了的。殿怜。头谜。
生命周期
組件周期鉤子函數(shù) | 說(shuō)明 |
---|---|
constructor(myService:MyService) | 類(lèi)的構(gòu)造器會(huì)再其他生命周期函數(shù)前調(diào)用柱告,在該方法中完成服務(wù)的依賴(lài)注入 |
ngOnChanges | 當(dāng)被綁定的輸入屬性的值發(fā)生變化時(shí)調(diào)用际度,首次調(diào)用一定會(huì)發(fā)生在 ngOnInit之前乖菱。(@input屬性(輸入屬性)發(fā)生變化時(shí)窒所,會(huì)調(diào)用吵取。非此屬性皮官,不會(huì)調(diào)用捺氢。) |
ngOnInit | 只執(zhí)行一次,dom操作可放在其中 |
ngDoCheck | 更新檢測(cè)機(jī)制郁岩,如果組件內(nèi)發(fā)生異步事件问慎,就會(huì)檢查整個(gè)組件樹(shù) |
ngAfterContentInit | 組件內(nèi)容初始化之后調(diào)用 |
ngAfterContentChecked | |
ngAfterViewInit | 組件視圖初始化后調(diào)用 |
ngAfterViewChecked | |
ngOnDestroy | 實(shí)例被銷(xiāo)毀前調(diào)用如叼,僅調(diào)用一次 |
進(jìn)入正文啦 ~
一、路由篇
1歇终、route設(shè)置三步走
(1)评凝、手動(dòng)添加路由文件
appRoutes的配置
import { Routes } from "@angular/router";
//引入組件等奕短。翎碑。。遣铝。
import { ChildComponent } from'./home/child/child.component'
//定義并輸出常量路由
export const appRoutes:Routes=[
//地址欄輸入 .../example翰蠢,加載組件ExampleComponentComponent
{ path:'example',component:ExampleComponentComponent},
{ path:'home',
component:HomeComponent,
children:[{//子路由 .../home/home-child,加載ChildComponent組件
path:'child',
component:ChildComponent,
//children:...
}]
},
{ path:'home/brother',component:BrotherComponent},//下圖示例
{
//如果 地址欄沒(méi)輸入定義的路由就跳轉(zhuǎn)到home路由界面
path:'',
redirectTo:'home',
pathMatch:'full'
}
]
child組件是通過(guò)一級(jí)路由被載入到homeComponent的html模板的<router-outlet>下方
<div routerLink="/example">栗子</div>
<div routerLink="./brother">brother</div>
<div routerLink="child">child的內(nèi)容會(huì)展示在當(dāng)頁(yè)面 router-outlet 中</div>
<!-- <div [routerLink]="['./child']">child的內(nèi)容會(huì)展示在當(dāng)頁(yè)面---另一種寫(xiě)法</div> -->
<router-outlet></router-outlet>
<router-outlet>
路由占位符,可以理解為渲染路由組件的區(qū)域蝇裤,一個(gè)組件只有一個(gè)無(wú)名的<router-outlet>
可以有多個(gè)有名的<router-outlet>
栓辜,例如:<router-outlet name=”a”>
藕甩、<router-outlet name=”b”>
(2)狭莱、app.module.ts文件中引用
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { RouterModule } from '@angular/router'
import { appRoutes } from './route.module'
import { AppComponent } from './app.component';
@NgModule({
declarations: [
AppComponent,
],
imports: [
//注入模塊中腋妙,forChild只能用于子路由,forRoot只能用于根模塊
// forRoot有一個(gè)可選的配置參數(shù)匙睹,里面有四個(gè)選項(xiàng)
// enableTracing :在console.log中打印出路由內(nèi)部事件信息
// useHash :把url改成hash風(fēng)格 /#/
// initialNavigation : 禁用初始導(dǎo)航痕檬,沒(méi)用過(guò)梦谜。改淑。
// errorHandler :使用自定義的錯(cuò)誤處理朵夏,來(lái)拋出報(bào)錯(cuò)信息榆纽;
RouterModule.forRoot(appRoutes,{useHash: true}), //添加 !饥侵!
BrowserModule,
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
(3)躏升、在index.html中添加
<body>
<app-root></app-root>
</body>
2膨疏、其他注意點(diǎn)
(1)佃却、路由跳轉(zhuǎn)中可加上參數(shù)
import { Component, OnInit } from '@angular/core';
import { Router } from '@angular/router';
@Component({
//忽略...
})
export class HomeComponent implements OnInit {
constructor(
public router:Router
) { }
goTopage(page,queryParams){
this.router.navigate([page],{ queryParams: queryParam })
}
}
ps:可通過(guò)navigate()方法來(lái)實(shí)現(xiàn)頁(yè)面跳轉(zhuǎn)
(2)饲帅、forChild的使用
根模塊中使用forRoot(),子模塊中使用forChild()
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [];
@NgModule({
imports: [
RouterModule.forChild(routes)
],
// ...
})
export class ChildModule { }
二灶泵、指令篇
-
結(jié)構(gòu)指令
1丘逸、 NgClass:接收一個(gè)對(duì)象深纲,key為class名,value為值儒喊,表示是否用改樣式
<p [ngClass]="{'fadeIn'}">example</p>
<p [ngClass]="{'fadeIn':!hidden}">example</p>
2怀愧、NgStyle:設(shè)置dom元素的css屬性芯义,可以動(dòng)態(tài)的
<div [ngStyle]="{'color':'#999'}">寫(xiě)樣式的東西咯</div>
<div [ngStyle]="{'color':gray?'#999':'#000'}">寫(xiě)樣式的東西咯</div>
3扛拨、NgFor:創(chuàng)建dom元素举塔,類(lèi)似ng1中的ng-repeat
const list = [ {name:'a'} ,{name:'b'}]
const array = [1,2,3,4]
<ul>
<li *ngFor="let item of list"></li>
</ul>
<ul>
<li *ngFor="let item of array,let i = index">第個(gè)</li>
</ul>
4、NgIf:設(shè)置dom元素的展示或隱藏
<div *ngIf="show">要不要展示咧渴频?<div>
-
自定義屬性指令
當(dāng)內(nèi)置指令還不能滿(mǎn)足實(shí)際業(yè)務(wù)場(chǎng)景時(shí)北启,Angular中提供了自定義指令來(lái)滿(mǎn)足特定的場(chǎng)景需求
舉個(gè)栗子:給用自定義屬性的內(nèi)容加點(diǎn)特殊處理
1暖庄、第一步培廓,創(chuàng)建一個(gè)directive文件
ng generate directive child-directive
此時(shí)app.module
中已增加ChildDirectiveDirective
指令
2肩钠、第二步价匠,在模塊中引入我們定義的指令
//...
@NgModule({
imports: [ BrowserModule ],
declarations: [
AppComponent,
ChildDirectiveDirective//!!申明我們定義的指令
],
bootstrap: [ AppComponent ]
})
//...
3踩窖、第三步晨横,擼出directive的邏輯
import { Directive,ElementRef,Renderer2,Input } from '@angular/core';
@Directive({//@Directive裝飾器指定了一個(gè)選擇器名稱(chēng)手形,用于指出與此指令相關(guān)聯(lián)的屬性的名字
selector: 'color,[color]'
})
export class ChildDirectiveDirective {
@Input('color')color : any;//@Input 為組件提供數(shù)據(jù)
private elem;
constructor(private renderer:Renderer2,elementRef:ElementRef) {
this.elem = elementRef//elementRef可直接獲取到dom
}
ngOnInit(){
this.renderer.setStyle(this.elem.nativeElement,'color',this.color)//讓頁(yè)面上該dom渲染該樣式
}
}
4库糠、第四步瞬欧,在html中引用該自定義屬性
組件中添加該指令,可自定義展示的樣式
<div [color]='"red"'>測(cè)試下咯</div>
三艘虎、組件
是時(shí)候祭出我的超簡(jiǎn)單基礎(chǔ)組件第一套G晏2持汀妄呕!switch組件的實(shí)現(xiàn)
look look 組件實(shí)現(xiàn)的部分
- 1绪励、 第一步疏魏,實(shí)現(xiàn)switch的樣式
首先把switch輪子的樣式寫(xiě)好
- switch.component.html
<span> <label class="iSwitch"> <input type="checkbox" (click)="switch($event)" #switchInput > <i></i> </label> </span>
具體css樣式怎么擼的我就不貼了,自行擼官份!下面主要講組件的實(shí)現(xiàn)
- 2舅巷、 第二步,實(shí)現(xiàn)switch輪子的邏輯
- switch.component.ts
import { Component, OnInit, Output, EventEmitter, Input, ViewChild,ElementRef,Renderer2 } from '@angular/core';
@Component({
selector:'switch',//selector就是使用該組件時(shí)候的標(biāo)簽名字
templateUrl:'./switch.component.html',
styleUrls:['./switch.component.scss']
})
export class SwitchComponent implements OnInit {
public open:boolean;
@ViewChild('switchInput' , {read: ElementRef}) switchInput: ElementRef;
//ElementRef 直接獲取了輪子中<input>dom元素
@Output() onchange: EventEmitter<any> = new EventEmitter<any>();
//前面提到@input,可以從demo子組件中獲取數(shù)據(jù)搁凸,@output則相當(dāng)于方法的綁定情屹,將onchange方法綁定到demo中垃你,在父作用域中處理事件。output一般都是一個(gè)EventEmitter實(shí)例皆刺,通過(guò)emit()方法將父組件取到的值返回給demo子組件中
constructor(private renderer: Renderer2 ,) { }
ngOnInit() {
}
switch(e){
this.open = !this.open
this.onchange.emit(this.switchInput.nativeElement.checked)//返回給子組件demo中
}
}
- 3羡蛾、 第三步锨亏,使用輪子的方式
使用輪子的代碼,使用的方法:
demo.component.html
<switch (onchange)="getSwitchVaule($event)"></switch>
demo.component.ts
export class SwitchDemo {
public getSwitchVaule(v){
//頁(yè)面中的onchange方法是父組件提供的
console.log(‘結(jié)果:’,v)
}
}
暫停一下捐迫,來(lái)一些啰嗦J┐鳌赞哗!
看到這是否還會(huì)一臉懵逼肪笋?乾颁??我們來(lái)解讀下實(shí)現(xiàn)的過(guò)程湿右。
使用<switch>
標(biāo)簽名毅人,可以實(shí)例化一個(gè)switch
組件丈莺。</p>
獲取到父組件通過(guò)@Output提供的onchange方法弛秋,在這個(gè)方法中蟹略,<switch>
父組件處理switch
開(kāi)關(guān)的值遏佣,處理后的值状婶。通過(guò)emit()
方法返還給demo組件中的 public getSwitchVaule(v)
方法中
- 4、第四步钓猬,給輪子增加點(diǎn)初始值
接著我們就會(huì)思考switch組件是否可以添加一些默認(rèn)值碴倾,進(jìn)入頁(yè)面默認(rèn)就是打開(kāi)的狀態(tài)逗噩。
也就是demo組件如何給父組件通信,告訴父組件我現(xiàn)在是打開(kāi)的狀態(tài)跌榔,這里可以借助添加屬性的方法來(lái)實(shí)現(xiàn)
<switch (onchange)="getSwitchVaule($event)" [default]=true>
[defalut]
是一個(gè)自定義的屬性异雁,并且傳值true,可在父組件輪子中去獲取demo的傳值
//...
export class SwitchComponent implements OnInit {
@Input('default') defaultData: any;//默認(rèn)關(guān)閉狀態(tài)
constructor(...) { }
ngOnInit(){
this.initDefault();//設(shè)置一個(gè)獲取初始默認(rèn)值的函數(shù)
}
initDefault(){
//this.defaultData 可獲取到demo組件中傳值過(guò)來(lái)的true
if(this.defaultData){
this.switchInput.nativeElement.checked = true;//設(shè)置頁(yè)面switch的開(kāi)關(guān)狀態(tài)
this.open = !this.open
}
}
}
//...
- 5僧须、最后一點(diǎn)纲刀,自行思考!5F健示绊!
現(xiàn)在switch組件已經(jīng)可以支持點(diǎn)擊打開(kāi)或者關(guān)閉開(kāi)關(guān),并且可接收一個(gè)默認(rèn)開(kāi)關(guān)值暂论。至于switch的禁用狀態(tài)disabled也同理可實(shí)現(xiàn),這個(gè)大家就自行思考啦~道理都是一樣的啦 ~~~