介紹
angularJs是第一版,angular是第二版以后的統(tǒng)稱。
angular不兼容angularJs娇昙。
HttpClient(基于RxJS響應(yīng)式編程)
相比于其他框架,更適用于復(fù)雜應(yīng)用具温。性能高,體積小筐赔。
初始化
官方文檔铣猩,快速上手
npm install -g @angular/cli
安裝腳手架
ng new my-app
創(chuàng)建項(xiàng)目
cd my-app
切換到項(xiàng)目
ng serve --open
啟動(dòng)項(xiàng)目
文件目錄結(jié)構(gòu)
tslint配置
tslint.json
組件體驗(yàn)
模塊
根模塊
作用:?jiǎn)?dòng)應(yīng)用
模塊:獨(dú)立、封閉的茴丰,模塊間的引用通過(guò)導(dǎo)入和導(dǎo)出來(lái)完成
模塊包含:組件达皿、服務(wù)、指令贿肩,這些要配置后才生效
@NgModule
根組件裝飾器
告訴angular將這個(gè)類當(dāng)作模塊處理
@NgModule{{元數(shù)據(jù)對(duì)象}}
@Component
組件裝飾器
數(shù)據(jù)綁定
插值表達(dá)式
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// templateUrl: './app.component.html',
template: '<div title="{{company}}">{{title}}--{{company}}--{{1+2}}-- {{fn()}}</div>',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
title = '塞納河后臺(tái)管理'
company = 'snh'
fn () {
return '方法返回內(nèi)容'
}
}
屬性綁定
插值表達(dá)式中的屬性綁定方式峦椰,在執(zhí)行時(shí)最終會(huì)轉(zhuǎn)換成這種屬性綁定
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// templateUrl: './app.component.html',
template: `
<a [href]="url">鏈接</a>
<input type="checkbox" [checked]="isChecked" />
`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
url = 'http://www.baidu.com'
isChecked = true
}
事件綁定
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// templateUrl: './app.component.html',
template: `
<button (click)="handleClick()" (mouseenter)="handleMouseEnter()">請(qǐng)點(diǎn)擊</button>
<a [href]="url" (click)="handleClick($event)">鏈接</a>
`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
url = 'http://www.baidu.com'
handleClick (event) {
// 阻止瀏覽器的默認(rèn)行為
event.preventDefault()
console.log('handleClick', event)
}
handleMouseEnter () {
console.log('handleMouseEnter')
}
}
雙向數(shù)據(jù)綁定
屬性綁定和事件綁定的結(jié)合
app.module.ts
app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
// templateUrl: './app.component.html',
template: `
<input type="text" [(ngModel)]="msg" />
<p>{{msg}}</p>
`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
msg = '我是默認(rèn)值'
}
語(yǔ)言服務(wù)
指令
組件:擁有模板的指令
屬性型指令:改變?cè)赝庥^和行為的指令
結(jié)構(gòu)型指令:添加和移除DOM元素,改變DOM布局的指令
[ngClass]
[ngStyle]
*ngIf
*ngFor
基本使用
隔行變色
使用trackBy
解決性能問(wèn)題
普通數(shù)據(jù)在渲染時(shí)沒(méi)有性能問(wèn)題汰规,對(duì)象數(shù)組在渲染時(shí)有性能問(wèn)題汤功。這時(shí)需要使用trackBy
來(lái)解決。
todos案例
組件通訊
ng generate component child
生成子組件溜哮,并自動(dòng)完成了相關(guān)的配置
父組件傳值給子組件
子組件傳值給父組件
子組件創(chuàng)建事件滔金,觸發(fā)事件,傳遞參數(shù)
父組件綁定事件茂嗓,接收參數(shù)
todos案例分離組件
ng generate module todos
創(chuàng)建todos模塊
目標(biāo)結(jié)構(gòu)如下
指定位置餐茵,創(chuàng)建三個(gè)子組件
ng generate component todos/todo
ng generate component todos/todo-header
ng generate component todos/todo-list
在根模塊中使用todos
模塊
然后將原來(lái)寫(xiě)在根模塊中的內(nèi)容,搬到
todos
模塊中即可述吸。注意:
FormsModule
也要搬過(guò)去忿族,否則todos
模塊中不能使用。
抽離todo-header
組件
要用到組件之間的通訊,子傳父 - 任務(wù)添加
抽離todo-list
組件
任務(wù)展示肠阱、刪除、狀態(tài)切換
要用到組件之間的通訊朴读,父?jìng)髯?- 拿到todos
數(shù)據(jù)
要用到組件之間的通訊屹徘,子傳父 - 刪除和修改狀態(tài)時(shí),要修改父組件中的數(shù)據(jù)衅金,為了保持?jǐn)?shù)據(jù)源的單一性
todo
父組件 - 提供代辦事項(xiàng)的數(shù)據(jù)
使用TypeScript
angular官方推薦使用ts
增強(qiáng)了項(xiàng)目的可維護(hù)性
有利于協(xié)作開(kāi)發(fā)
ts語(yǔ)法
- 類型注解
let id: number
- 接口
// 創(chuàng)建接口
interface Todo {
id: number,
name: string,
done: boolean
}
export class TodoComponent implements OnInit {
constructor() { }
// 任務(wù)列表
todos: Todo[] = [
{ id: 1, name: '玩游戲啊', done: true },
{ id: 2, name: '點(diǎn)外賣呀', done: false },
{ id: 3, name: '看bilibili', done: false }
]
}
- 泛型
@Output()
add = new EventEmitter<string>()
- 類成員修飾符
private todoName: string
接口的另一種使用方式
服務(wù)
組件:
提供數(shù)據(jù)綁定的屬性和方法
服務(wù):
處理業(yè)務(wù)邏輯噪伊,供組件使用,如從服務(wù)器獲取數(shù)據(jù)氮唯、驗(yàn)證用戶輸入等
組件是服務(wù)的消費(fèi)者
服務(wù)說(shuō)明:
-
@injectable()
裝飾器來(lái)表示一個(gè)服務(wù) - 服務(wù)要注冊(cè)提供商才能使用
- angular通過(guò)依賴注入(DI)來(lái)位組件提供服務(wù)
- DI提供要使用的服務(wù)即可鉴吹。不需要手動(dòng)創(chuàng)建服務(wù)實(shí)例
- 推薦在
constructor
中提供組件中用到的服務(wù)
服務(wù)的創(chuàng)建和基本使用
ng generate service todos/todos
生成服務(wù)
在todos.service.ts
中,如下是服務(wù)的提供商惩琉,必須有@Injectable
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class TodosService {
constructor() { }
// 提供方法
todoTest () {
console.log('test-TodoService')
}
}
使用方式豆励,todo.component.ts
,這樣瞒渠,當(dāng)點(diǎn)擊添加的時(shí)候良蒸,即可調(diào)用服務(wù)
// 導(dǎo)入服務(wù)
import { TodosService } from '../todos.service'
export class TodoComponent implements OnInit, OnChanges {
constructor(private todoService: TodosService) { }
addTodo(todoName: string) {
// 演示服務(wù)的調(diào)用
this.todoService.todoTest()
}
ngOnInit() {
}
ngOnChanges() {
}
}
注冊(cè)提供商的三種方式
- 根級(jí)提供商,在所有組件中都可以調(diào)用
@Injectable({
providedIn: 'root'
})
- 模塊內(nèi)可用的提供商
todos.service.ts
@Injectable()
todos.module.ts
// 導(dǎo)入服務(wù)
import { TodosService } from './todos.service'
@NgModule({
providers: [
TodosService
]
})
- 組件內(nèi)可用的提供商(其子組件也可以使用伍玖,兄弟組件不可使用)
todos.service.ts
@Injectable()
todo.component.ts
// 導(dǎo)入服務(wù)
import { TodosService } from './../todos.service';
@Component({
providers: [TodosService]
})
export class TodoComponent implements OnInit, OnChanges {
constructor(private todoService: TodosService) { }
addTodo(todoName: string) {
// 演示服務(wù)的調(diào)用
this.todoService.todoTest()
}
ngOnInit() {
}
ngOnChanges() {
}
}
todos案例使用服務(wù)修改
把組件中的業(yè)務(wù)邏輯抽離到服務(wù)中
HttpClient
angular是內(nèi)置的客戶端嫩痰,不是使用第三方的。
- 作用: 發(fā)送http請(qǐng)求
- 封裝了XMLHttpRequest
- 使用基于可觀察(Observable)對(duì)象的api
- 提供了請(qǐng)求和響應(yīng)攔截器
- 流式錯(cuò)誤處理機(jī)制
HttpClient的基本使用
app.module.ts
中導(dǎo)入
// 導(dǎo)入HttpClient模塊
import {HttpClientModule} from '@angular/common/http'
@NgModule({
imports: [
HttpClientModule
],
})
app.component.html
觸發(fā)事件
<div>
<button (click)="getData()">獲取數(shù)據(jù)</button>
<h3>通過(guò)HttpClient獲取到的數(shù)據(jù)是:{{name}}</h3>
</div>
app.component.ts
接口是用的assets
中的json
文件模擬的
// 導(dǎo)入HttpClient
import { HttpClient } from '@angular/common/http'
export class AppComponent {
constructor(private http: HttpClient) { }
name: string
getData () {
this.http.get('../assets/todos.json').subscribe((res: any) => {
console.log(res)
this.name = res.name
})
}
}
添加類型檢查后的app.component.ts
窍箍,這種寫(xiě)法比較嚴(yán)謹(jǐn)
interface Todo {
name: string,
description: string
}
export class AppComponent {
constructor(private http: HttpClient) { }
name: string
getData () {
this.http.get<Todo>('../assets/todos.json').subscribe((res: Todo) => {
console.log(res)
this.name = res.name
})
}
}
獲取完整的響應(yīng)
使用{ observe: 'response' }
即可獲取完整的響應(yīng)
類型檢查時(shí)要使用HttpResponse
// 導(dǎo)入HttpClient
import { HttpClient, HttpResponse } from '@angular/common/http'
export class AppComponent {
constructor(private http: HttpClient) { }
name: string
getData() {
this.http.get<Todo>('../assets/todos.json', { observe: 'response' })
.subscribe((res: HttpResponse<Todo>) => {
console.log(res)
console.log(res.headers.get('content-type'), res.body)
this.name = res.body.name
})
}
}
錯(cuò)誤處理
getData() {
this.http.get<Todo>('../assets/todos.json1', { observe: 'response' })
.subscribe((res: HttpResponse<Todo>) => {
console.log(res)
console.log(res.headers.get('content-type'), res.body)
this.name = res.body.name
},
err => {
console.log(err)
}
)
}
json-server
json-server官方文檔
npm install -g json-server
安裝包
新建db.json
文件串纺,在里面寫(xiě)好json數(shù)據(jù)
{
"todos": [
{ "id": 1, "name": "玩游戲啊", "done": true },
{ "id": 2, "name": "點(diǎn)外賣啊", "done": false },
{ "id": 3, "name": "看bilibii", "done": false }
]
}
json-server --watch db.json
這就是接口的地址
其他請(qǐng)求
接口地址使用的是json-server
生成的地址
app.component.html
<div>
<button (click)="getData()">get獲取</button>
<button (click)="addData()">post增加</button>
<button (click)="delData()">del刪除</button>
<button (click)="updateData()">patch修改</button>
</div>
app.component.ts
import { Component } from '@angular/core';
// 導(dǎo)入HttpClient
import { HttpClient } from '@angular/common/http'
interface Todo {
name: string,
description: string
}
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.scss']
})
export class AppComponent {
constructor(private http: HttpClient) { }
name: string
url = 'http://localhost:3000/todos'
// get
getData() {
this.http.get(this.url).subscribe(res => {
console.log(res)
})
}
// post
addData() {
this.http.post(this.url,{
name: '測(cè)試測(cè)試',
done: true
}).subscribe(res => {
console.log('post success:', res)
})
}
// del
delData() {
this.http.delete(`${this.url}/2`).subscribe(res => {
console.log('delete success:', res);
})
}
// patch
updateData() {
this.http.patch(`${this.url}/3`,{
name: '黑呀~我是修改后的數(shù)據(jù)呀~'
}).subscribe(res => {
console.log('patch success:', res)
})
}
}
todos案例 HttpClient
路由
實(shí)現(xiàn)SPA(單頁(yè)應(yīng)用程序)的基礎(chǔ)設(shè)施
URL和組件的對(duì)應(yīng)規(guī)則
angular使用HTML5風(fēng)格(history.pushState)的導(dǎo)航
支持:重定向、路由高亮椰棘、通配符路由纺棺、路由參數(shù)、子路由晰搀、路由模塊五辽、路由守衛(wèi)、異步路由
路由的基本使用
index.html
<base href="/">
導(dǎo)入
RouterModule
配置路由規(guī)則
appRoutes
-
根模塊中配置
forRoot
說(shuō)明:
路由服務(wù)應(yīng)該是單例的外恕,但在路由懶加載等場(chǎng)景下會(huì)造成服務(wù)多次注冊(cè)杆逗,所以使用forRoot()
方法導(dǎo)入模塊,保證項(xiàng)目中只有一個(gè)Router
服務(wù) 添加路由出口
<router-outlet></router-outlet>
配置更多路由
新建組件
配置多個(gè)路由規(guī)則即可
默認(rèn)路由
{ path: '', redirectTo: '/home', pathMatch: 'full' }
通配符路由
用來(lái)匹配沒(méi)有的路由規(guī)則鳞疲,跳轉(zhuǎn)到404頁(yè)面
// 通配符路由罪郊,要放在最后面,不然所有的路由都不能正常匹配
{ path: '**', component: PageNotFoundComponent }`
編程式導(dǎo)航
import { Component, OnInit } from '@angular/core';
// 導(dǎo)入路由提供的服務(wù)
import { Router } from '@angular/router'
@Component({
selector: 'app-page-not-found',
templateUrl: './page-not-found.component.html',
styleUrls: ['./page-not-found.component.css']
})
export class PageNotFoundComponent implements OnInit {
// 注入服務(wù)
constructor(private router: Router) { }
time = 5
ngOnInit() {
const timerId = setInterval( () => {
this.time--
if (this.time === 0) {
clearInterval(timerId)
// 編程式導(dǎo)航
this.router.navigate(['/home'])
}
}, 1000)
}
}
路由的參數(shù)
ng g c car
新建模塊
配路由規(guī)則尚洽,:id
表示路由參數(shù)
{
path: 'car/:id',
component: CarComponent
},
app.component.html
中設(shè)置跳轉(zhuǎn)
<ul>
<li>
<a routerLink="car/1">野馬</a>
</li>
<li>
<a routerLink="car/2">法拉利</a>
</li>
<li>
<a routerLink="car/3">蘭博基尼</a>
</li>
<li>
<a routerLink="car/4">奇瑞QQ</a>
</li>
</ul>
car.component.ts
中獲取路由參數(shù)
import { Component, OnInit } from '@angular/core';
// 導(dǎo)入路由服務(wù)
import { ActivatedRoute } from '@angular/router'
@Component({
selector: 'app-car',
templateUrl: './car.component.html',
styleUrls: ['./car.component.css']
})
export class CarComponent implements OnInit {
constructor(private route: ActivatedRoute) { }
ngOnInit() {
this.route.paramMap.subscribe(param => {
// param中可拿到當(dāng)前路由跳轉(zhuǎn)的參數(shù)
const id = param.get('id')
console.log(param, id);
})
}
}
子路由
新建組件
配置路由規(guī)則
{
path: 'home',
component: HomeComponent,
children: [{
path: 'home/child',
component: HomeChildComponent
}]
},
給子路由出口悔橄, home.component.html
<p>
home works!
<a routerLink="home/child">子路由</a>
<router-outlet></router-outlet>
</p>
路由激活高亮
<!-- 在css中設(shè)置actived的樣式,即為高亮樣式 -->
<!-- [routerLinkActiveOptions]是精確匹配才高亮 -->
<a routerLink='/home' routerLinkActive="actived" [routerLinkActiveOptions]="{exact: true}">首頁(yè)</a>
<a routerLink='/about' routerLinkActive="actived">關(guān)于</a>
表單
響應(yīng)式表單
很強(qiáng)大,推薦
模型驅(qū)動(dòng)癣疟,數(shù)據(jù)驅(qū)動(dòng)視圖的思想
同步的數(shù)據(jù)訪問(wèn)挣柬,保證數(shù)據(jù)和視圖是一致的、可預(yù)測(cè)的
增強(qiáng)了可測(cè)試性睛挚,讓測(cè)試變得簡(jiǎn)單
內(nèi)置表單驗(yàn)證器模板驅(qū)動(dòng)表單
數(shù)據(jù)雙向綁定實(shí)現(xiàn)
vue angular.js
響應(yīng)式表單
- 導(dǎo)入響應(yīng)式表單模塊
import { ReactiveFormsModule } from '@angular/forms';
- 生成并導(dǎo)入一個(gè)新的表單控件
- 在模板中注冊(cè)該控件
- 更新用戶名和獲取用戶名方法
// 獲取用戶名
getUserName () {
console.log(this.username.value)
}
// 更新用戶名
setUserName () {
this.username.setValue('fdd')
}
表單驗(yàn)證
-
內(nèi)置表單驗(yàn)證器
在username.errors
中拿值邪蛔,判斷是否通過(guò)校驗(yàn)
在username.dirty
中拿值,判斷是否輸入過(guò)
<p *ngIf="username.dirty && username.errors?.required">用戶名為必填項(xiàng)</p>
在hasError()
中拿到值扎狱,判斷是否通過(guò)校驗(yàn)
ngOnInit () {
console.log(this.username)
console.log(this.username.hasError('required'))
}
多個(gè)條件的校驗(yàn)
password = new FormControl('123', [
Validators.required,
Validators.minLength(4)
])
<p *ngIf="password.dirty && password.errors?.minlength">密碼格式不正確</p>
- 自定義表單驗(yàn)證器
// 自定義表單驗(yàn)證
nickname = new FormControl('', [this.nicknameValidate])
nicknameValidate(control) {
console.log(control);
if (/^[a-z]{3,6}$/.test(control.value)) {
return null
}
return { error: true }
}
<p *ngIf="nickname.dirty && nickname.hasError('error')">昵稱格式不正確</p>
FormGroup
import { Component, OnInit } from '@angular/core';
// 導(dǎo)入表單控件
import { FormControl, Validators, FormGroup } from '@angular/forms';
@Component({
selector: 'app-form-group',
templateUrl: './form-group.component.html',
styleUrls: ['./form-group.component.css']
})
export class FormGroupComponent implements OnInit {
constructor() { }
loginForm = new FormGroup({
username: new FormControl('', Validators.required),
password: new FormControl('123', [
Validators.required,
Validators.minLength(4)
])
});
onSubmit() {
if (this.loginForm.valid) {
console.log('submit');
} else {
console.log('err');
}
}
ngOnInit() {
// console.log(this.loginForm)
}
}
<div>
<form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
<label>
<span>用戶名:</span>
<input type="text" formControlName="username" />
<span>{{this.loginForm.value.username}}</span>
</label>
<label>
<span>密碼:</span>
<input type="text" formControlName="password" />
<span>{{this.loginForm.value.password}}</span>
</label>
<button type="submit">提交</button>
</form>
<p *ngIf="this.loginForm.controls.username.dirty && this.loginForm.controls.username.errors?.required">用戶名為必填項(xiàng)</p>
<p *ngIf="this.loginForm.controls.password.dirty && this.loginForm.controls.password.errors?.minlength">密碼格式不正確</p>
</div>
FormBuilder
生成表單控件的便捷方法
導(dǎo)入侧到、注入
loginForm = this.fb.group({
username: ['', Validators.required],
password: ['123', [
Validators.required,
Validators.minLength(4)
]]
});