以前用Angular的时候也只是用的js构建的应用,还未涉及ts;趁这次有机会,有时间用ts、angular的脚手架工具angular-cli构建一个经典的todo案例;几乎所有的vue、react的基础经典案例都离不开Todo List的应用构建。所以这次也是以Todo案例,来简单了解下ts构建单页面应用,以及请求响应逻辑处理。这次后端的逻辑主要采用前端工具json-server伪装处理后端逻辑,返回REST API;
最终效果预览图:
1.安装 Angular-cli
2.初始化一个自定义的anglar项目
3.全局安装json-server
4.定义基本的数据库json文件
5.开启项目服务,开启json后台服务
注:
1.这部分可以参考我的其他博文:
https://blog.csdn.net/WU5229485/article/details/82917389
https://blog.csdn.net/WU5229485/article/details/83028007
2.json数据库文件(db,json)
{
"todos": [
{
"id": 1,
"title": "Read SitePoint article",
"complete": false
},
{
"id": 2,
"title": "Clean inbox",
"complete": false
},
{
"id": 3,
"title": "Make restaurant reservation",
"complete": false
}
]
}
1.创建开发环境和生产环境的分别对应的URL
(1)在开发环境和生产环境存储不同的API URL
(2)Angular-cli 默认提供两种环境配置:
src/environments/environment.ts # 开发环境
src/environments/environment.prod.ts. # 上线环境
(3)命令行动态加载( 就不用手动改代码啦 ~ )
ng serve 或者 ng build 默认加载开发环境 src/environments/environment.ts
ng serve --environment prod 或者 ng build --environment prod 默认加载上线环境 src/environments/environment.prod.ts
2.创建后台增删改查处理逻辑(基类、控制类)
(1)创建Todo基类,定义应用所需要的公共属性
export class Todo {
id: number;
title = '';
complete = false;
constructor(values: Object = {}) {
Object.assign(this, values);
}
}
(2)创建 TodoDataService 控制类,AppServer类中的方法,向其AppServer类方法中传递Todo参数
import {Injectable} from '@angular/core';
import {Todo} from './todo';
@Injectable()
export class TodoDataService {
// Placeholder for last id so we can simulate
// automatic incrementing of ids
lastId: number = 0;
// Placeholder for todos
todos: Todo[] = [];
constructor() {
}
// POST /todos
addTodo(todo: Todo): TodoDataService {}
// DELETE /todos/:id
deleteTodoById(id: number): TodoDataService {}
// PUT /todos/:id
updateTodoById(id: number, values: Object = {}): Todo {}
// GET /todos
getAllTodos(): Todo[] {}
// GET /todos/:id
getTodoById(id: number): Todo {}
// Toggle todo complete
toggleTodoComplete(todo: Todo){}
}
(3)创建TodoDataService 控制类的单元测试
import { TestBed, inject } from '@angular/core/testing';
import { TodoDataService } from './todo-data.service';
import { ApiService } from './api.service';
import { ApiMockService } from './api-mock.service';
describe('TodoDataService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [
TodoDataService,
{
provide: ApiService,
useClass: ApiMockService
}
]
});
});
it('should ...', inject([TodoDataService], (service: TodoDataService) => {
expect(service).toBeTruthy();
}));
});
3. 创建与后台联调的AppServer类,在类中实现对db.json数据库文件的增删改查,然后在TodoDataService实例化AppSrever类,调用其内部公共方法,返回db.json中的数据,赋值给Todo类中的属性
import { Injectable } from '@angular/core';
import { environment } from 'environments/environment';
import { Http, Response } from '@angular/http';
import { Todo } from './todo';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/observable/throw';
const API_URL = environment.apiUrl;
@Injectable()
export class ApiService {
constructor(
private http: Http
) {
}
public getAllTodos(): Observable {
return this.http
.get(API_URL + '/todos')
.map(response => {
const todos = response.json();
return todos.map((todo) => new Todo(todo));
})
.catch(this.handleError);
}
public createTodo(todo: Todo): Observable {
return this.http
.post(API_URL + '/todos', todo)
.map(response => {
return new Todo(response.json());
})
.catch(this.handleError);
}
public getTodoById(todoId: number): Observable {
return this.http
.get(API_URL + '/todos/' + todoId)
.map(response => {
return new Todo(response.json());
})
.catch(this.handleError);
}
public updateTodo(todo: Todo): Observable {
return this.http
.put(API_URL + '/todos/' + todo.id, todo)
.map(response => {
return new Todo(response.json());
})
.catch(this.handleError);
}
public deleteTodoById(todoId: number): Observable {
return this.http
.delete(API_URL + '/todos/' + todoId)
.map(response => null)
.catch(this.handleError);
}
private handleError (error: Response | any) {
console.error('ApiService::handleError', error);
return Observable.throw(error);
}
}
1. 一共包括下列组件:
Todos 最外层盒子:app.component
Todos 头部:todo-list-header
Todos 脚部:todo-list-footer
Todos 列表盒子:todo-list
Todos 单个列表项:todo-list-item
2. 新增一条Todo列表项逻辑,涉及组件(todo-list-header、app.component):
(1)在todo-list-header中,回车实现新增操作
2-1-1.结构:
Todos
2-1-2. 逻辑操作
import { Component, Output, EventEmitter } from '@angular/core';
import { Todo } from '../todo';
@Component({
selector: 'app-todo-list-header',
templateUrl: './todo-list-header.component.html',
styleUrls: ['./todo-list-header.component.css']
})
export class TodoListHeaderComponent {
newTodo: Todo = new Todo();
@Output()
add: EventEmitter = new EventEmitter();
constructor() {
}
addTodo() {
this.add.emit(this.newTodo);
this.newTodo = new Todo();
}
}
(2)app.component组件中调用todo-list-header,并且传递方法绑定
2-2-1.结构:
2-2-2. 逻辑操作:
onAddTodo(todo) {
this.todoDataService
.addTodo(todo)
.subscribe(
(newTodo) => {
this.todos = this.todos.concat(newTodo);
}
);
}
(3)然后把更新的todos传递给子组件todo-list-footer、todo-list
3. 删除和更新一条Todo列表项逻辑
(1)触发删除和更改选中状态的组件todo-list-item
3-1-1. 结构:
3-1-2. 逻辑处理
@Input() todo: Todo;
@Output()
remove: EventEmitter = new EventEmitter();
@Output()
toggleComplete: EventEmitter = new EventEmitter();
constructor() {
}
toggleTodoComplete(todo: Todo) {
this.toggleComplete.emit(todo);
console.log(todo);
}
removeTodo(todo: Todo) {
this.remove.emit(todo);
}
(2)由于toggleComplete和remove方法是父组件todo-list传递过来的方法,所以需要在负组件中实现
3-2-1. todo-list组件中调用toto-list-item组件
0">
-
3-2-2. 父组件todo-list逻辑处理
@Input()
todos: Todo[];
@Output()
remove: EventEmitter = new EventEmitter();
@Output()
toggleComplete: EventEmitter = new EventEmitter();
constructor() {
}
onToggleTodoComplete(todo: Todo) {
this.toggleComplete.emit(todo);
}
onRemoveTodo(todo: Todo) {
this.remove.emit(todo);
}
(3)在组件todo-list中,remove和toggleComplete方法依旧是其父组件传递过来,所以需要在todo-list父组件app.component组件中实现
3-3-1. 结构组件调用
3-3-2. 逻辑处理
onToggleTodoComplete(todo) {
this.todoDataService
.toggleTodoComplete(todo)
.subscribe(
(updatedTodo) => {
todo = updatedTodo;
}
);
}
onRemoveTodo(todo) {
this.todoDataService
.deleteTodoById(todo.id)
.subscribe(
(_) => {
this.todos = this.todos.filter((t) => t.id !== todo.id);
}
);
}
* 数据传递总结:
整个数据经历了两次连续传递:
第一次: 组件todo-list-item传递到todo-list中
第二次: 组件todo-list传递到app.component中
https://github.com/RiversCoder/angular-todo-app