Open jiayisheji opened 6 years ago
五一假期过后,Angular6发布正式版,相关联的UI组件库Material 和 脚手架CLI,也一并发布6。 升级核心依赖:
工作中已经完成一个Angular6项目,这里来写一个简单的Angular6教程。
古语云:君子谋而后动,三思而后行。我们做一个功能,先规划功能细节。
先看个效果图:
在本文中,我们将构建一个Angular6 Todo web应用程序,允许用户:
这是一个演示应用程序,我们将一步步从零构建它们。
这里所有的代码都是公开的,所以你可以使用这些代码。
这是一个在线编辑器预览
让我们开始吧!
这里看官网文档 快速开始。详细安装指南,这里不在一一介绍。
修改一下package.json
package.json
"scripts": { "start": "ng serve --open", ... },
这样可以直接使用npm start启动开发服务器并且自动打开默认浏览器并访问 http://localhost:4200/。
npm start
http://localhost:4200/
现在我们有了Angular-CLI,我们可以使用它来生成Todo应用程序:
Angular-CLI
这里是生成项目的文档
ng new angular-todolist --style=scss
说明:生成一个angular-todolist项目,css预处理器用scss。
angular-todolist
满足我们的Todo应用程序的需要,我们需要:
我们所有相关应用都放在todoApp组件,在app组件里面使用todoApp组件。
这里是生成文件的文档
生成组件
ng g c todo-app
这样在app文件夹里面就出现todo-app文件夹
生成服务
ng g s todo-app/todo
注意:默认生成的文件都是以app为开始路径,我们需要放在todo-app里,所以是todo-app/todo
app
todo-app
todo-app/todo
生成类
ng g cl todo-app/todo
注意:生成组件是c,生成类是cl。
c
cl
生成指令
ng g d todo-app/after-view-focus
我们现在的todo-app文件夹里结构应该是:
after-view-focus.directive.spec.ts after-view-focus.directive.ts todo-app.component.html todo-app.component.scss todo-app.component.spec.ts todo-app.component.ts todo.service.spec.ts todo.service.ts todo.ts
因为我们使用TypeScript,我们可以使用一个类来表示Todo项目。
让我们打开src/app/todo.ts并将其内容替换为:
src/app/todo.ts
export class Todo { id: number; value: string; done: boolean = false; edit: boolean = false; constructor(values: Object = {}) { Object.assign(this, values); } }
我们需要设计数据结构,每个Todo项有三个属性:
构造函数逻辑允许我们在实例化过程中指定属性值:
let todo = new Todo({ title: 'The first todos', done: false });
我们可以测试一下,Angular-CLI提供单元测试和e2e测试,默认生成类文件不会带测试文件,我们需要手动创建一个todo.spec.ts文件。
todo.spec.ts
import { Todo } from './todo'; describe('Todo', () => { it('应该创建一个实例', () => { expect(new Todo()).toBeTruthy(); }); it('应该在构造函数中接受值', () => { const todo = new Todo({ value: 'hello', done: true }); expect(todo.value).toEqual('hello'); expect(todo.done).toEqual(true); expect(todo.edit).toEqual(false); }); });
为了保证不受干扰,删除app.component.spec.ts文件,把todo-app.component.spec.ts文件里面代码都注释起来。
app.component.spec.ts
todo-app.component.spec.ts
为了验证我们的代码是否按预期工作,我们现在可以运行单元测试:
npm test
这将执行业力来运行所有单元测试。如果单元测试失败,可以联系我。
现在我们有了一个Todo类,让我们创建一个Todo服务来为我们管理所有的Todo项。
TodoService将负责管理我们的Todo项目。
在以后的文章中,我们将看到如何与REST API通信,但是现在我们将把所有数据存储在内存存储中。
REST API
现在,我们可以将todo管理逻辑添加到src/app/todo.services.ts中的TodoService中
src/app/todo.services.ts
import { Injectable } from '@angular/core'; import { Todo } from './todo'; @Injectable() export class TodoService { // Placeholder for todo's todos: Todo[] = []; /** Used to generate unique ID's */ nextId = 0; constructor() { } // Simulate POST /todos addTodo(todo: Todo): TodoService { todo.id = Date.now(); this.todos.push(todo); return this; } // Simulate DELETE /todos/:id deleteTodoById(id: number): TodoService { this.todos = this.todos .filter(todo => todo.id !== id); return this; } // Simulate POST /todos/delete deleteAllTodo(): TodoService { this.todos = this.todos .filter(todo => !todo.done); return this; } // Simulate PUT /todos/:id updateTodoById(id: number, values: Object = {}): Todo { const todo = this.getTodoById(id); if (!todo) { return null; } Object.assign(todo, values); return todo; } // Simulate GET /todos getAllTodos(): Todo[] { return this.todos; } // Simulate GET /todos/done getAllDoneTodos(): Todo[] { return this.todos.filter(todo => todo.done); } // Simulate GET /todos/:id getTodoById(id: number): Todo { return this.todos .filter(todo => todo.id === id) .pop(); } // Toggle todo done toggleTodoDone(todo: Todo) { const updatedTodo = this.updateTodoById(todo.id, { done: !todo.done }); return updatedTodo; } }
我们已经完成必备的服务,实际的实现细节的方法不是本文的目的所必需的。这是主要表达意思, 我们的业务逻辑集中在服务。
确保我们的逻辑是预期,我们将单元测试添加到src/app/todo.service.spec中。
src/app/todo.service.spec
Angular-cli已为我们生成测试模板,我们只需要关心如何实现测试:
import { inject, TestBed } from '@angular/core/testing'; import { Todo } from './todo'; import { TodoService } from './todo.service'; describe('Todo Service', () => { beforeEach(() => { TestBed.configureTestingModule({ imports: [], providers: [TodoService] }); }); describe('#getAllTodos()', () => { it('应该默认返回一个空数组', inject([TodoService], (service: TodoService) => { expect(service.getAllTodos()).toEqual([]); })); it('应该返回所有待办事项', inject([TodoService], (service: TodoService) => { const todo1 = new Todo({ value: 'Hello 1', done: false }); const todo2 = new Todo({ value: 'Hello 2', done: true }); service.addTodo(todo1); service.addTodo(todo2); expect(service.getAllTodos()).toEqual([todo2, todo1]); })); }); describe('#save(todo)', () => { it('应该自动分配一个时间戳的ID', inject([TodoService], (service: TodoService) => { const todo1 = service.addTodo(new Todo({ value: 'Hello 1', done: false })); const todo2 = service.addTodo(new Todo({ value: 'Hello 2', done: true })); expect(service.getTodoById(todo1.id)).toEqual(todo1); expect(service.getTodoById(todo2.id)).toEqual(todo2); })); }); describe('#deleteTodoById(id)', () => { it('应该删除相应ID的待办事项', inject([TodoService], (service: TodoService) => { const todo1 = service.addTodo(new Todo({ value: 'Hello 1', done: false })); const todo2 = service.addTodo(new Todo({ value: 'Hello 2', done: true })); expect(service.getAllTodos()).toEqual([todo2, todo1]); service.deleteTodoById(todo1.id); expect(service.getAllTodos()).toEqual([todo2]); service.deleteTodoById(todo2.id); expect(service.getAllTodos()).toEqual([]); })); it('如果没有找到使用相应ID的待办事项,则不应删除任何内容', inject([TodoService], (service: TodoService) => { const todo1 = service.addTodo(new Todo({ value: 'Hello 1', done: false })); const todo2 = service.addTodo(new Todo({ value: 'Hello 2', done: true })); expect(service.getAllTodos()).toEqual([todo2, todo1]); service.deleteTodoById(3); expect(service.getAllTodos()).toEqual([todo2, todo1]); })); }); describe('#updateTodoById(id, values)', () => { it('应该返回相应ID和更新的数据todo', inject([TodoService], (service: TodoService) => { const todo = service.addTodo(new Todo({ value: 'Hello 1', done: false })); const updatedTodo = service.updateTodoById(todo.id, { value: 'new value' }); expect(updatedTodo.value).toEqual('new value'); })); it('如果未找到待办事项应该返回null', inject([TodoService], (service: TodoService) => { const todo = service.addTodo(new Todo({ value: 'Hello 1', done: false })); const updatedTodo = service.updateTodoById(2, { value: 'new value' }); expect(updatedTodo).toEqual(null); })); }); describe('#toggleTodoDone(todo)', () => { it('应该返回更新后的待办事项与完成状态', inject([TodoService], (service: TodoService) => { const todo = new Todo({ value: 'Hello 1', done: false }); service.addTodo(todo); const updatedTodo = service.toggleTodoDone(todo); expect(updatedTodo.done).toEqual(true); service.toggleTodoDone(todo); expect(updatedTodo.done).toEqual(false); })); }); });
检查我们的业务逻辑是否有效,我们运行单元测试:
好了,现在我们有一个可以通过测试的TodoService,是时候实现应用程序的主要部分了。
组件是Angular最小的单元了,整个Angular应用就是一个颗组件树构成。
Angular
我们生成项目时候,angular-cli默认为我们创建了app-root根组件,我们现在生产的app-todo-app,放到app.component.html里面,删除其他html。
app-root
app-todo-app
app.component.html
一个组件是有3部分组建成:
模板和样式也可以内联脚本文件中指定。Angular-CLI默认创建单独的文件,所以在本文中我们将使用单独的文件。
import { Component } from '@angular/core'; @Component({ selector: 'app-todo-app', templateUrl: './todo-app.component.html', styleUrls: ['./todo-app.component.scss'] }) export class TodoAppComponent implements OnInit { constructor() { } }
我们先来添加组件的视图todo-app.component.html
todo-app.component.html
<header class="header"> <h1>Todos</h1> <form class="todo-form" (ngSubmit)="addTodo()"> <input class="add-todo" [(ngModel)]="newTodo" name="first" placeholder="What needs to be done?" required="required" autocomplete="off"> <button type="submit" class="add-btn" *ngIf="newTodo.length">+</button> </form> </header> <main class="main" *ngIf="todos.length"> <input class="toggle-all" type="checkbox" [(ngModel)]="allDone" (ngModelChange)="toggleAllTodoDone($event)"> <ul class="todo-list"> <li *ngFor="let todo of todos" [class.completed]="todo.done" (dblclick)="editingTodo(todo)"> <div class="view" *ngIf="!todo.edit"> <input class="toggle" type="checkbox" [checked]="todo.done" (click)="toggleDoneTodo(todo)"> <label>{{ todo.value }}</label> <button class="destroy" (click)="destroyTodo(todo)"></button> </div> <input class="edit" *ngIf="todo.edit" appAfterViewFocus [value]="todo.value" #edit (blur)="cancelEditingTodo(todo)" placeholder="What do you need to write?" (keyup.enter)="editedTodo(todo, edit)"> </li> </ul> </main> <footer class="footer" *ngIf="todos.length"> <span class="todo-count"> <strong>{{ todoCount }}</strong> <span> items left</span> </span> <button class="clear-completed" (click)="destroyAllTodo()" [class.clear-operate]="clearCount"> <span>Clear </span> <strong>{{ clearCount }}</strong> <span> done items</span> </button> </footer>
来简单说一下Angular模板语法:
更多的Angular模板语法,你应该阅读官方的文档模板的语法。
让我们一一介绍:
整个模板分为3个结构块: header,main,footer;
header,main,footer
先说输入创建一个新的待办事项:
<form class="todo-form" (ngSubmit)="addTodo()"> <input class="add-todo" [(ngModel)]="newTodo" name="first" placeholder="What needs to be done?" required="required" autocomplete="off"> <button type="submit" class="add-btn" *ngIf="newTodo.length">+</button> </form>
不要担心newTodo或addTodo()从哪里来,我们很快就会讲到那里,现在只需要试着去理解的模板语法。
接下来是一段显示待办事项:
<main class="main" *ngIf="todos.length"></main>
在这个部分中,我们循环一个元素来显示每个待办事项:
<li *ngFor="let todo of todos" [class.completed]="todo.done" (dblclick)="editingTodo(todo)"></li>
最后我们显示待办事项的细节为每个ngFor中的待办事项:
<div class="view" *ngIf="!todo.edit"> <input class="toggle" type="checkbox" [checked]="todo.done" (click)="toggleDoneTodo(todo)"> <button class="destroy" (click)="destroyTodo(todo)"></button> </div> <input class="edit" *ngIf="todo.edit" appAfterViewFocus [value]="todo.value" #edit (blur)="cancelEditingTodo(todo)" placeholder="What do you need to write?" (keyup.enter)="editedTodo(todo, edit)">
appAfterViewFocus是angular属性型指令,在 Angular 中有三种类型的指令:
注意: 为什么要写这个指令,它作用是什么?它作用是当编辑时,input出现时候,自动获取焦点,不用用户再次去点击输入框,触发获取焦点事件,还有一个更重要的原因,如果没有焦点,失去焦点事件就无法执行,这样输入就不会被隐藏。它的写法很简单:
import { Directive, AfterViewInit, ElementRef } from '@angular/core'; @Directive({ selector: '[appAfterViewFocus]' }) export class AfterViewFocusDirective implements AfterViewInit { constructor(private elementRef: ElementRef) { } ngAfterViewInit() { this.elementRef.nativeElement.focus(); } }
.nativeElement
HTMLElement
#edit是angular模板引用变量;
#edit
注意: 这里拿到就是input这个dom,我们可以直接操作获取它上面的属性和方法。
为什么不用双向绑定[(ngModel)]?
[(ngModel)]
什么是双向绑定: 数据模型(Module)和视图(View)之间的双向绑定。
如果使用双向绑定,我们修改以后,我们数据就直接跟着一起被修改,那么我们要操作取消操作怎么办,增加一个临时的属性来记录它,取消时候就直接回滚,确认就直接清除这个临时属性。
如果不使用双向绑定,我们先赋值给视图,视图修改以后,我们的数据还没有变,取消操作直接取消就行,确认操作,拿到dom引用,把dom的值去更新数据。
关于全选效果
<input class="toggle-all" type="checkbox" [(ngModel)]="allDone" (ngModelChange)="toggleAllTodoDone($event)">
我们来说最后一块统计结构:
<footer class="footer" *ngIf="todos.length"> <span class="todo-count"> <strong>{{ todoCount }}</strong> <span> items left</span> </span> <button class="clear-completed" (click)="destroyAllTodo()" [class.clear-operate]="clearCount"> <span>Clear </span> <strong>{{ clearCount }}</strong> <span> done items</span> </button> </footer>
clear-operate
模板我们已经介绍完,关于css不是我们重点,这里忽略讲解。
接下来我们该介绍todo-app.component.ts:
todo-app.component.ts
首先需要引入依赖
import { TodoService } from './todo.service'; import { Todo } from './todo';
接下来就是angular特色之一依赖注入,这里不过多介绍。
@Component({ selector: 'app-todo-app', templateUrl: './todo-app.component.html', styleUrls: ['./todo-app.component.scss'], providers: [TodoService] }) export class TodoAppComponent { newTodo: string = ''; constructor( private todoService: TodoService ) { }
注意:providers可以在模块下,也可以在组件里,这也限定他们使用范围。模块里面注册,适用于该模块下所有的组件,服务,指令等;组件里面注册,只适用于当前组件和子组件。
每当视图中输入值的变化,更新组件实例的价值。当组件实例中的值改变,视图中输入元素中的值的变化。
接下来,我们实现我们在视图中使用的所有方法。
/** * add todo * @memberof TodoAppComponent */ addTodo(): void { if (!this.newTodo) { return alert('What do you need to write?'); } this.todoService.addTodo(new Todo({ value: this.newTodo })); this.newTodo = ''; } /** * destroy todo * @memberof TodoAppComponent */ destroyTodo(todo: Todo): void { this.todoService.deleteTodoById(todo.id); } /** * destroy done todo * @memberof TodoAppComponent */ destroyAllTodo(): void { if (!this.clearCount) { return; } if (!confirm('Do you need to delete the selected one?')) { return; } this.todoService.deleteAllTodo(); } /** * toggle todo done * @memberof TodoAppComponent */ toggleDoneTodo(todo: Todo): void { this.todoService.toggleTodoDone(todo); } /** * toggle all todo done * @memberof TodoAppComponent */ toggleAllTodoDone(event: boolean): void { this.todos.forEach(item => item.done = event); } /** * editing todo * @memberof TodoAppComponent */ editingTodo(todo: Todo): void { if (!todo.done) { todo.edit = true; } } /** * cancel editing todo * @memberof TodoAppComponent */ cancelEditingTodo(todo: Todo): void { todo.edit = false; } /** * edited todo * @memberof TodoAppComponent */ editedTodo(todo: Todo, input: HTMLInputElement): void { todo.value = input.value; todo.edit = false; } /** * get todos * @memberof TodoAppComponent */ get todos(): Todo[] { return this.todoService.getAllTodos(); } /** * get todos all done be get todos * @memberof TodoAppComponent */ get allDone(): boolean { const todos = this.todos; return todos.length && todos.filter(item => item.done).length === todos.length; } /** * get todos all not done number * @memberof TodoAppComponent */ get todoCount(): number { return this.todos.filter(item => !item.done).length; } /** * get todos all done number * @memberof TodoAppComponent */ get clearCount(): number { return this.todos.filter(item => item.done).length; }
newTodo
这里有4个 get,在Typescript叫存取器, 通过getters/setters来截取对对象成员的访问。 它能帮助我们有效的控制对对象成员的访问。这里只要控制器里面值发送变化,模板就会更着改变,很方便。
get
Typescript
存取器
注意:无论是服务还是组件里,都是需要熟练使用原生数据操作方法,比如数组,对象,字符串等。这里主要使用数组相关方法,如果你对这些还不熟,请赶紧去提升一下。es6以后又新增很多方法,操作数据会更方便。angular是数据驱动,如果不会玩转操作,基本很难继续下去。
功能很小,应该不言自明todoService我们代表所有的业务逻辑。
委派业务逻辑服务是良好的编程实践,因为它能让我们集中管理和测试业务逻辑。
我们还为大家编写一个E2E测试用例,可以查阅e2e文件里面文件,运行命名npm run e2e即可。
E2E
e2e
npm run e2e
github给我们每个项目都运行有一个预览页面,我们叫它github-pages。
github-pages
ng build --prod --base-href https://jiayisheji.github.io/angular-todolist/
注意:github-pages预览地址是 你的用户名.github.io/你的项目名/
你的用户名.github.io/你的项目名/
--base-href:修改html里面的base的href属性,如果有路由必须要使用的。
base
href
gh-pages
git add -f dist && git commit -n -m \"(release): git-pages\" && git subtree push --prefix dist origin gh-pages
注意:就是打包以后的目录,需要特别注意一下,angular-cli6是一个多工程的脚手架,打包后生成的是dist/angular-todolist,我们最终需要上传是这个文件夹里面的内容,那么就需要改脚本。
dist/angular-todolist
git add -f dist && git commit -n -m \"(release): git-pages\" && git subtree push --prefix dist/angular-todolist origin gh-pages
git push --follow-tags origin master
我在所有项目里面都会用到它
"_github": "ng build --prod --base-href https://jiayisheji.github.io/angular-todolist/", "_publish": "git add -f dist && git commit -n -m \"(release): git-pages\" && git subtree push --prefix dist/angular-todolist origin gh-pages", "git-pages": "npm run _github && npm run _publish" "release":"git push --follow-tags origin master"
运行命令
npm run git-pages
npm run release
代码提交需要去github,项目下设置里面开启github-pages.
GitHub Pages
gh-pages branch
save
就好出现你的Github Pages链接,你可以做代码演示,分享给其他小伙伴观看,也可以做静态blog。
Github Pages
blog
注意:一旦启用就不能再选择none,只能你删除项目。你删除分支,访问就会出现404。
毫无疑问,Angular是一个平台。一个非常强大前端框架!
我们讨论了许多让我们回顾所学在本文中:
麻雀虽小,五脏俱全,Todo应用看起来,功能很简单,其实它里面功能可以做很多衍生,都是我们平常业务需要的,比如购物车, 全选等。
这个有个类似的变种需求功能:我也不知道叫什么名字,antd里面叫穿梭框。这就留个大家一个作业吧。
穿梭框
如果你不知道如何下手,可以跟我交流。
五一假期过后,Angular6发布正式版,相关联的UI组件库Material 和 脚手架CLI,也一并发布6。 升级核心依赖:
工作中已经完成一个Angular6项目,这里来写一个简单的Angular6教程。
古语云:君子谋而后动,三思而后行。我们做一个功能,先规划功能细节。
先看个效果图:
在本文中,我们将构建一个Angular6 Todo web应用程序,允许用户:
这是一个演示应用程序,我们将一步步从零构建它们。
这里所有的代码都是公开的,所以你可以使用这些代码。
这是一个在线编辑器预览
让我们开始吧!
快速开始
这里看官网文档 快速开始。详细安装指南,这里不在一一介绍。
修改一下
package.json
这样可以直接使用
npm start
启动开发服务器并且自动打开默认浏览器并访问http://localhost:4200/
。生成我们的Todo应用程序
现在我们有了
Angular-CLI
,我们可以使用它来生成Todo应用程序:生成我们的Todo应用程序
这里是生成项目的文档
说明:生成一个
angular-todolist
项目,css预处理器用scss。生成文件
满足我们的Todo应用程序的需要,我们需要:
我们所有相关应用都放在todoApp组件,在app组件里面使用todoApp组件。
这里是生成文件的文档
生成组件
这样在app文件夹里面就出现todo-app文件夹
生成服务
注意:默认生成的文件都是以
app
为开始路径,我们需要放在todo-app
里,所以是todo-app/todo
生成类
注意:生成组件是
c
,生成类是cl
。生成指令
我们现在的
todo-app
文件夹里结构应该是:创建Todo类
因为我们使用TypeScript,我们可以使用一个类来表示Todo项目。
让我们打开
src/app/todo.ts
并将其内容替换为:我们需要设计数据结构,每个Todo项有三个属性:
构造函数逻辑允许我们在实例化过程中指定属性值:
我们可以测试一下,
Angular-CLI
提供单元测试和e2e测试,默认生成类文件不会带测试文件,我们需要手动创建一个todo.spec.ts
文件。为了验证我们的代码是否按预期工作,我们现在可以运行单元测试:
这将执行业力来运行所有单元测试。如果单元测试失败,可以联系我。
现在我们有了一个Todo类,让我们创建一个Todo服务来为我们管理所有的Todo项。
创建Todo服务
TodoService将负责管理我们的Todo项目。
在以后的文章中,我们将看到如何与
REST API
通信,但是现在我们将把所有数据存储在内存存储中。现在,我们可以将todo管理逻辑添加到
src/app/todo.services.ts
中的TodoService中我们已经完成必备的服务,实际的实现细节的方法不是本文的目的所必需的。这是主要表达意思, 我们的业务逻辑集中在服务。
确保我们的逻辑是预期,我们将单元测试添加到
src/app/todo.service.spec
中。Angular-cli已为我们生成测试模板,我们只需要关心如何实现测试:
检查我们的业务逻辑是否有效,我们运行单元测试:
好了,现在我们有一个可以通过测试的TodoService,是时候实现应用程序的主要部分了。
创建TodoApp组件
组件是
Angular
最小的单元了,整个Angular
应用就是一个颗组件树构成。我们生成项目时候,angular-cli默认为我们创建了
app-root
根组件,我们现在生产的app-todo-app
,放到app.component.html
里面,删除其他html。一个组件是有3部分组建成:
模板和样式也可以内联脚本文件中指定。Angular-CLI默认创建单独的文件,所以在本文中我们将使用单独的文件。
我们先来添加组件的视图
todo-app.component.html
来简单说一下Angular模板语法:
更多的Angular模板语法,你应该阅读官方的文档模板的语法。
让我们一一介绍:
整个模板分为3个结构块:
header,main,footer
;先说输入创建一个新的待办事项:
接下来是一段显示待办事项:
在这个部分中,我们循环一个元素来显示每个待办事项:
最后我们显示待办事项的细节为每个ngFor中的待办事项:
appAfterViewFocus是angular属性型指令,在 Angular 中有三种类型的指令:
注意: 为什么要写这个指令,它作用是什么?它作用是当编辑时,input出现时候,自动获取焦点,不用用户再次去点击输入框,触发获取焦点事件,还有一个更重要的原因,如果没有焦点,失去焦点事件就无法执行,这样输入就不会被隐藏。它的写法很简单:
.nativeElement
拿到就是一个HTMLElement
, 这里是input这个dom。#edit
是angular模板引用变量;注意: 这里拿到就是input这个dom,我们可以直接操作获取它上面的属性和方法。
为什么不用双向绑定
[(ngModel)]
?什么是双向绑定: 数据模型(Module)和视图(View)之间的双向绑定。
如果使用双向绑定,我们修改以后,我们数据就直接跟着一起被修改,那么我们要操作取消操作怎么办,增加一个临时的属性来记录它,取消时候就直接回滚,确认就直接清除这个临时属性。
如果不使用双向绑定,我们先赋值给视图,视图修改以后,我们的数据还没有变,取消操作直接取消就行,确认操作,拿到dom引用,把dom的值去更新数据。
关于全选效果
我们来说最后一块统计结构:
clear-operate
模板我们已经介绍完,关于css不是我们重点,这里忽略讲解。
接下来我们该介绍
todo-app.component.ts
:首先需要引入依赖
接下来就是angular特色之一依赖注入,这里不过多介绍。
注意:providers可以在模块下,也可以在组件里,这也限定他们使用范围。模块里面注册,适用于该模块下所有的组件,服务,指令等;组件里面注册,只适用于当前组件和子组件。
每当视图中输入值的变化,更新组件实例的价值。当组件实例中的值改变,视图中输入元素中的值的变化。
接下来,我们实现我们在视图中使用的所有方法。
newTodo
属性值注意:无论是服务还是组件里,都是需要熟练使用原生数据操作方法,比如数组,对象,字符串等。这里主要使用数组相关方法,如果你对这些还不熟,请赶紧去提升一下。es6以后又新增很多方法,操作数据会更方便。angular是数据驱动,如果不会玩转操作,基本很难继续下去。
功能很小,应该不言自明todoService我们代表所有的业务逻辑。
委派业务逻辑服务是良好的编程实践,因为它能让我们集中管理和测试业务逻辑。
我们还为大家编写一个
E2E
测试用例,可以查阅e2e
文件里面文件,运行命名npm run e2e
即可。部署到GitHub页面
github给我们每个项目都运行有一个预览页面,我们叫它
github-pages
。提交代码
注意:github-pages预览地址是
你的用户名.github.io/你的项目名/
--base-href:修改html里面的
base
的href
属性,如果有路由必须要使用的。gh-pages
分支注意:就是打包以后的目录,需要特别注意一下,angular-cli6是一个多工程的脚手架,打包后生成的是
dist/angular-todolist
,我们最终需要上传是这个文件夹里面的内容,那么就需要改脚本。我在所有项目里面都会用到它
运行命令
代码提交需要去github,项目下设置里面开启
github-pages
.开启 Github-pages
GitHub Pages
gh-pages branch
save
.注意:一旦启用就不能再选择none,只能你删除项目。你删除分支,访问就会出现404。
总结
毫无疑问,Angular是一个平台。一个非常强大前端框架!
我们讨论了许多让我们回顾所学在本文中:
麻雀虽小,五脏俱全,Todo应用看起来,功能很简单,其实它里面功能可以做很多衍生,都是我们平常业务需要的,比如购物车, 全选等。
这个有个类似的变种需求功能:我也不知道叫什么名字,antd里面叫
穿梭框
。这就留个大家一个作业吧。如果你不知道如何下手,可以跟我交流。