angular 的優(yōu)點(diǎn)
- 組織化前端結(jié)構(gòu)
- 強(qiáng)大和新穎
- 完整的解決方案(路由、HTTP、RxJS等)
- 構(gòu)建強(qiáng)大單頁應(yīng)用
- MVC設(shè)計(jì)模式
- Tyepscript
- 極好的cli工具
預(yù)備知識(shí)
- Typescript
- Classes
- 高階函數(shù) - forEach、map、filter
- 箭頭函數(shù)
- Promise
- MVC設(shè)計(jì)模式
the angular way
- 使用Typescript(變量、函數(shù)、參數(shù))
- 基于組件
component based - 使用
service來共享組件間的數(shù)據(jù)/功能 -
modules的概念(root module, forms modules, http module, etc) - 使用RxJS的
observables來異步操作。(使用內(nèi)置的HTTP模塊發(fā)送請求,在組件中訂閱返回的Observables) - 較陡的學(xué)習(xí)曲線
angular 基本用法
創(chuàng)建項(xiàng)目
ng new myapp
啟動(dòng)項(xiàng)目
ng serve
打包項(xiàng)目
ng build
創(chuàng)建組件
ng generate component todos
創(chuàng)建服務(wù)
ng generate service todo
創(chuàng)建模塊
ng generate module <moduleName>
在Angular的大型應(yīng)用中可以使用ngrx和Redux等狀態(tài)管理工具
項(xiàng)目準(zhǔn)備
安裝angular
sudo npm install -g @angular/cli
驗(yàn)證安裝
ng --version
新建項(xiàng)目
ng new todolist
運(yùn)行項(xiàng)目
ng serve --open
index.html
單頁應(yīng)用入口,可以在這個(gè)文件中引入CDN,<app-root>是組件的根標(biāo)簽
angular.json
項(xiàng)目配置文件,如打包目錄(outputPath),靜態(tài)資源目錄(assets),樣式目錄(styles)
app.module.ts
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule,
AppRoutingModule
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule { }
Angular中所有module必須導(dǎo)入后才能使用,如果使用cli創(chuàng)建模塊,會(huì)自動(dòng)進(jìn)行導(dǎo)入
imports使其他模塊的導(dǎo)出聲明在當(dāng)前模塊中可用declarations使當(dāng)前模塊中的指令(包括組件和管道)可用于當(dāng)前模塊中的其他指令providers讓依賴注入知道services和value,它們添加到root scope,并且注入到依賴它們的服務(wù)或指令bootstrap數(shù)組聲明哪些組件需要插入到index.html中
將多個(gè)組件插入到index.html中
index.html
<app-root></app-root>
<test-root></test-root>
test.component.ts
import { Component } from "@angular/core";
@Component({
selector: "test-root",
templateUrl: "./app.component.html",
styleUrls: ["./app.component.css"],
})
export class TestComponent {
title = "test";
}
app.module.ts
// ...
import { TestComponent } from "./test.component";
@NgModule({
declarations: [AppComponent, TestComponent],
imports: [BrowserModule, AppRoutingModule],
providers: [],
bootstrap: [AppComponent, TestComponent],
})
export class AppModule {}
基礎(chǔ)語法
創(chuàng)建組件
ng generate component components/Todos
ng g c components/TodoItem
// 創(chuàng)建服務(wù)
ng g s services/Todo
該命令會(huì)在components文件夾下創(chuàng)建新的組件
屬性綁定
[屬性名]="屬性值"
app/components/todo-item/todo-item.component.html
<div [ngClass]="setClasses()">
<p>
<input
(change)="onToggle(todo)"
type="checkbox"
[checked]="todo.completed"
/>
{{ todo.title }}
<button (click)="onDelete(todo)" class="del">x</button>
</p>
</div>
*ngFor循環(huán)語句
app/components/todos/todos.component.html
<app-todo-item *ngFor="let todo of todos" [todo]="todo"> </app-todo-item>
向子組件傳參
[參數(shù)名]="參數(shù)值"向子組件傳值
app/components/todos/todos.component.html
<app-todo-item *ngFor="let todo of todos" [todo]="todo"> </app-todo-item>
[todo]="todo"向app-todo-item組件傳值
接收參數(shù)
app/components/todo-item/todo-item.component.ts
export class TodoItemComponent implements OnInit {
@Input() todo: Todo;
}
在模板中使用參數(shù)
app/components/todo-item/todo-item.component.html
<div [ngClass]="setClasses()">
<p>
<input
(change)="onToggle(todo)"
type="checkbox"
[checked]="todo.completed"
/>
{{ todo.title }}
<button (click)="onDelete(todo)" class="del">x</button>
</p>
</div>
向父組件傳參
app/components/todo-item/todo-item.component.ts
import { Component, OnInit, Input, EventEmitter, Output } from "@angular/core";
import { Todo } from "../../models/Todo";
import { TodoService } from "../../services/todo.service";
@Component({
selector: "app-todo-item",
templateUrl: "./todo-item.component.html",
styleUrls: ["./todo-item.component.css"],
})
export class TodoItemComponent implements OnInit {
// 從父組件接收參數(shù)
@Input() todo: Todo;
// 向父組件傳遞參數(shù)
@Output() deleteTodo: EventEmitter<Todo> = new EventEmitter();
constructor(private todoService: TodoService) {}
ngOnInit(): void {}
setClasses() {
let classes = {
todo: true,
"is-completed": this.todo.completed,
};
return classes;
}
// onToggle
onToggle(todo) {
// Toggle on UI
todo.completed = !todo.completed;
// Toggle on server
this.todoService.toggleCompleted(todo).subscribe((todo) => {
console.log(todo);
});
}
// 通過 emit 向父組件傳參
onDelete(todo) {
this.deleteTodo.emit(todo);
}
}
app/components/todos/todos.component.html
<app-todo-item
*ngFor="let todo of todos"
[todo]="todo"
(deleteTodo)="deleteTodo($event)"
>
</app-todo-item>
(deleteTodo)監(jiān)聽這個(gè)事件的名稱和子組件的EventEmitter對象名必須一致
ngClass動(dòng)態(tài)添加類
在HTML元素上添加或者移除CSS類
app/components/todo-item/todo-item.component.html
<div [ngClass]="setClasses()">
<p>
<input type="checkbox" />
<button class="del">x</button>
</p>
</div>
其中setClasses()是腳本文件中的一個(gè)方法
app/components/todo-item/todo-item.component.ts
export class TodoItemComponent implements OnInit {
@Input() todo: Todo;
constructor() {}
ngOnInit(): void {}
setClasses() {
let classes = {
todo: true,
"is-completed": this.todo.completed,
};
return classes;
}
}
監(jiān)聽事件
app/components/todo-item/todo-item.component.html
<div [ngClass]="setClasses()">
<p>
<input (change)="onToggle(todo)" type="checkbox" />
{{ todo.title }}
<button (click)="onDelete(todo)" class="del">x</button>
</p>
</div>
(change)監(jiān)聽輸入框改變事件,(click)監(jiān)聽元素點(diǎn)擊事件
網(wǎng)絡(luò)請求
導(dǎo)入HTTP請求模塊
app.module.ts
import { HttpClientModule } from "@angular/common/http";
@NgModule({
declarations: [AppComponent, TodosComponent, TodoItemComponent],
imports: [BrowserModule, AppRoutingModule, HttpClientModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
在services發(fā)送請求
app/services/todo.service.ts
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Todo } from "../models/Todo";
import { Observable } from "rxjs";
@Injectable({
providedIn: "root",
})
export class TodoService {
todoUrl: string = "https://jsonplaceholder.typicode.com/todos?_limit=5";
constructor(private http: HttpClient) {}
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(this.todoUrl);
}
}
表單
導(dǎo)入表單模塊
app.module.ts
import { FormsModule } from "@angular/forms";
@NgModule({
declarations: [
AppComponent,
TodosComponent,
TodoItemComponent,
HeaderComponent,
AddTodoComponent,
],
imports: [BrowserModule, AppRoutingModule, HttpClientModule, FormsModule],
providers: [],
bootstrap: [AppComponent],
})
export class AppModule {}
在表單元素中使用雙向數(shù)據(jù)綁定
app/components/add-todo/add-todo.component.html
<form class="form" (ngSubmit)="onSubmit()">
<input
type="text"
name="title"
[(ngModel)]="title"
placeholder="Add Todo..."
/>
<input type="submit" value="submit" class="btn" />
{{ title }}
</form>
[(ngModel)]用于雙向數(shù)據(jù)綁定
(ngSubmit)監(jiān)聽表單提交事件
監(jiān)聽表單提交事件
app/components/add-todo/add-todo.component.ts
import { Component, OnInit, Output, EventEmitter } from "@angular/core";
@Component({
selector: "app-add-todo",
templateUrl: "./add-todo.component.html",
styleUrls: ["./add-todo.component.css"],
})
export class AddTodoComponent implements OnInit {
title: string;
@Output() addTodo: EventEmitter<any> = new EventEmitter();
constructor() {}
ngOnInit(): void {}
onSubmit() {
const todo = {
title: this.title,
completed: false,
};
// 觸發(fā) addTodo 事件
this.addTodo.emit(todo);
}
}
表單提交事件處理
**app/components/todos/todos.component.html **
<app-add-todo (addTodo)="addTodo($event)"></app-add-todo>
<app-todo-item
*ngFor="let todo of todos"
[todo]="todo"
(deleteTodo)="deleteTodo($event)"
>
</app-todo-item>
app/services/todo.service.ts
import { Injectable } from "@angular/core";
import { HttpClient, HttpHeaders } from "@angular/common/http";
import { Todo } from "../models/Todo";
import { Observable } from "rxjs";
const httpOptions = {
headers: new HttpHeaders({
"Content-Type": "application/json",
}),
};
@Injectable({
providedIn: "root",
})
export class TodoService {
todoUrl: string = "https://jsonplaceholder.typicode.com/todos";
todoLimit = "?_limit=5";
constructor(private http: HttpClient) {}
getTodos(): Observable<Todo[]> {
return this.http.get<Todo[]>(`${this.todoUrl}${this.todoLimit}`);
}
// Toggle Completed
// 發(fā)送json類型數(shù)據(jù)必須帶請求頭
toggleCompleted(todo: Todo): Observable<any> {
const url = `${this.todoUrl}/${todo.id}`;
return this.http.put(url, todo, httpOptions);
}
deleteTodo(todo: Todo): Observable<Todo> {
const url = `${this.todoUrl}/${todo.id}`;
return this.http.delete<Todo>(url, httpOptions);
}
addTodo(todo: Todo): Observable<Todo> {
return this.http.post<Todo>(this.todoUrl, todo, httpOptions);
}
}
路由
app/app-routing.module.ts
import { NgModule } from "@angular/core";
import { Routes, RouterModule } from "@angular/router";
import { TodosComponent } from "./components/todos/todos.component";
import { AboutComponent } from "./components/about/about.component";
const routes: Routes = [
{
path: "",
component: TodosComponent,
},
{
path: "about",
component: AboutComponent,
},
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule],
})
export class AppRoutingModule {}
app/app.component.html
<app-header></app-header>
<!-- <app-todos></app-todos> -->
<router-outlet></router-outlet>
路由跳轉(zhuǎn)
app/components/layout/header/header.component.html
<header class="header">
<h1>TODO</h1>
<a routerLink="/">Home</a>
<a routerLink="/about">About</a>
</header>