Angular项目 TodoMVC
起步
-
准备
- 下载静态模板
git clone https://github.com/tastejs/todomvc-app-template.git --depth 1
- 在静态文件根目录下
npm install
- 初始化项目
ng new todomvc-angular
- 在进行基础的选择之后,
ctrl+c
打断下载 - 打开新建的项目目录,CMD进入
cnpm install
- 启动
npm start
或者ng serve
- angular后台服务器开启
http://localhost:4200/
- 下载静态模板
-
将静态模板装载到angular脚手架生成的项目文件中
- 将静态文件中的
index.html
文件HTML骨架复制粘贴到项目app文件夹下的app.component.html
文件中 - 在项目文件的根目录中引入安装包
todomvc-common todomvc-app-css
,cnpm i todomvc-common todomvc-app-css
- 在项目文件根目录中,找到
angular.json
文件,找到styles
全局配置文件路径文件,打开此路径中的文件,引入全局css配置,依赖文件- 这里载入的第三方包中的样式文件可以不写路径,直接写第三方包名称路径即可
- 这里的CSS样式也可以写入局部CSS配置中
@import url('todomvc-app-css/index.css');
@import url('todomvc-common/base.css');
- 至此,静态页面完全移植至angular项目中
- 将静态文件中的
-
TodoMVC需求说明
- 数据列表显示
- 有数据
- 无数据
- 添加任务
- 页面初始化的时候文本框获得焦点
- 敲回车添加到任务列表中
- 不允许有非空数据
- 添加完成清空文本框
- 标记所有任务完成/未完成
- 任务项
- 切换任务完成状态
- 删除任务项
- 双击label进入编辑模式
- 编辑任务项
- 编辑文本框自动获取焦点
- 在编辑文本框中敲回车或者失去焦点
- 输入状态按下esc取消编辑
- 显示所有未完成任务数
- 模板逻辑
- 方法
- 计算属性
- 清除所有已完成任务
- 将数据持久化到localStorage中
- 路由状态切换
- 点击链接过滤数据的输出
- 刷新保持过滤状态
- 切换点击链接的样式
- 数据列表显示
-
angular列表循环的写法
*ngFor = "表达式"
<li *ngFor="let todo of todos">{{todo.title}}</li>
-
angular条件渲染
*ngIf="todos.length"
- 若是在某标签中写上为假的判断
*ngIf="false"
,则此标签隐藏
-
angular模板语法之层级包裹
- 原先写法,在需要的模块架构之上,加上统一的div,然后写上判断语句
*ngIf="todos.length"
- 模板语法:将div改成
<ng-template [ngIf]="todos.length"></ng-template>
- 原先写法,在需要的模块架构之上,加上统一的div,然后写上判断语句
<ng-template [ngIf]="todos.length"></ng-template>
- angular事件的写法
(keyup.enter)="addTodo($event)"
- 在
addTodo
事件函数中,this指向数据对象,这点和Vue相似,事件指向数据
<input
class="new-todo"
placeholder="What needs to be done?"
autofocus
(keyup.enter)="addTodo($event)"
>
- angular双向数据绑定
- 在主模块中引入
import { FormsModule } from '@angular/forms';
- 在主模块下面的依赖列表中加入:
FormsModule
[(ngModel)]="todo.done"
- 在主模块中引入
<input
class="toggle"
type="checkbox"
[(ngModel)]="todo.done"
>
- angular样式绑定
[ngClass]="{completed: todo.done}"
<li
*ngFor="let todo of todos"
[ngClass]="{completed: todo.done}"
>
get set
属性存取器get
数据写入页面- 当所有的t.done为真时,返回数据true,写入checked中
set
数据取出页面- 当input状态改变时,将checked的值传入函数,然后子类每项赋值
// 子开关控制总开关,全选总开关选中
// 所有的t.done为真时,返回数据,存入checked
get toggleAll(){
return this.todos.every(t => t.done)
}
// 总开关控制子开关,全选全不选
// 当总开关触发时`$event.target.checked`
// 当input状态改变时,将checked的值传入函数,然后每项赋值
set toggleAll(val){
this.todos.forEach(t => t.done = val)
}
- angular获取当前索引
- 在for循环时,拿下索引变量i
- 将索引变量传参给事件函数
removeTodo(i)
- 索引函数得到当前索引值
<li
*ngFor="let todo of todos; let i = index;"
[ngClass]="{completed: todo.done}"
>
<button
class="destroy"
(click)='removeTodo(i)'
></button>
</li>
removeTodo(index: number){
this.todos.splice(index, 1)
console.log(index);
}
- 删除当前项方法
removeTodo(index: number){
this.todos.splice(index, 1)
}
ngClass
属性类名: 布尔判断
<li
*ngFor="let todo of todos; let i = index;"
[ngClass]="{
completed: todo.done,
editing: currentEditing === todo
}"
></li>
- 关于选择当前对象
- 在循环对象创建中,是一个对象数组
- 因此,额外的创建一个空对象用来装载当前数据,以索引值为契机
- 然后,将装载的数据在对象数组中遍历匹配
- 匹配成功,即为选中当前对象
- 取消选择,只要将临时的对象清空即可
const todos = [
{
id: 1,
title: '摄影',
done: true
},
{
id: 2,
title: '设计',
done: true
},
{
id: 3,
title: '程序',
done: false
}
]
// 定义对象数组
public todos: {
id: number,
title: string,
done: boolean
}[] = todos
// 定义匹配对象
public currentEditing: {
id: number,
title: string,
done: boolean
} = null
<!-- 将匹配对象在数组对象中遍历寻找,相同即执行 -->
<li
*ngFor="let todo of todos; let i = index;"
[ngClass]="{
completed: todo.done,
editing: currentEditing === todo
}"
>
<!-- 鼠标双击后,将当前值传给匹配对象 -->
<label
(dblclick)="currentEditing = todo"
>{{ todo.title }}</label>
</li>
- 解构赋值
e
就是$event
的传参
const {keyCode, target} = e;
- 过滤器
- 过滤器中的回调函数用箭头函数,可以避免this问题
get remaningCount(){
return this.todos.filter(t => !t.done).length
}
-
获取标签内容
e.srcElement.innerHTML
e.srcElement.innerText
-
实现导航切换数据过滤的功能
public visibility: string = "all"
get filterTodos(){
if(this.visibility === 'all'){
return todos
}else if(this.visibility === 'active'){
return todos.filter(t => !t.done)
}else if(this.visibility === 'completed'){
return todos.filter(t => t.done)
}
}
-
初始化钩子函数
- 初始化钩子函数
ngOnInit()
- 该函数是一个特殊的Angular生命周期钩子函数
- 他会在Anhular应用初始化的时候执行一次
- 初始化钩子函数
-
哈希函数
- 哈希函数即是描点函数
ngOnInit(){
// Angular初始化时,监听hash的变化
// 此处要用箭头函数
window.onhashchange = () => {
// 当用户点击锚点时,我们需要获取当前的锚点链接
// 然后动态的将根组件中的visibility设置为当前点击的锚点标识
const hash = window.location.hash;
console.log(hash);
}
}
- 实现导航切换数据过滤的功能
- 提供一个属性,该属性会根据当前点击的链接返回过滤之后的数据
- filterTodos
- 提供一个属性,用来存储当前点击的链接标识
- visibility 是一个字符串
- all active completed
- 为链接添加点击事件,当点击导航链接的时候,改变
- 在方法内写出选择判断,当点击某个值时,将选项匹配给visibility
- 改变li遍历创建时的规则,由全部创建改为,filterTodos方法返回的数据
- 提供一个属性,该属性会根据当前点击的链接返回过滤之后的数据
<li
*ngFor="let todo of filterTodos; let i = index;"
[ngClass]="{
completed: todo.done,
editing: currentEditing === todo
}"
>
</li>
public visibility: string = "all"
ngOnInit(){
// Angular初始化时,监听hash的变化
// 此处要用箭头函数
window.onhashchange = () => {
// 当用户点击锚点时,我们需要获取当前的锚点链接
// 然后动态的将根组件中的visibility设置为当前点击的锚点标识
const hash = window.location.hash.substr(1)
switch(hash){
case '/':
this.visibility = 'all'
break
case '/active':
this.visibility = 'active'
break
case '/completed':
this.visibility = 'completed'
break
}
}
}
get filterTodos(){
if(this.visibility === 'all'){
return todos
}else if(this.visibility === 'active'){
return todos.filter(t => !t.done)
}else if(this.visibility === 'completed'){
return todos.filter(t => t.done)
}
}
- 刷新保存过滤状态
- 将以上代码中,哈希判断的代码作为另一个方法
- 在钩子函数初始化时,执行一次哈希判断
- 然后再在哈希事件中调用
- 由于在哈希事件中,this指向window,因此,此时的this需要bind绑定
ngOnInit(){
// Angular初始化时,监听hash的变化
// 此处要用箭头函数
this.hashchangeHandler();
window.onhashchange = this.hashchangeHandler.bind(this)
}
hashchangeHandler(){
const hash = window.location.hash.substr(1)
switch(hash){
case '/':
this.visibility = 'all'
break
case '/active':
this.visibility = 'active'
break
case '/completed':
this.visibility = 'completed'
break
}
}
-
数据改变触发的钩子函数
- ngDoCheck()
- 当数据发生变化时,刷新数据
-
数据本地永久化存储
todos = JSON.parse(window.localStorage.getItem('todos') || '[]')
- 将字符串转化为JSON数组
- 从本地数据库window.localStorage读取数据
window.localStorage.setItem('todos', JSON.stringify(this.todos))
- 将JSON数组转化为字符串
- 将页面更新后的数据存储到本地数据库window.localStorage中
public todos: {
id: number,
title: string,
done: boolean
}[] = JSON.parse(window.localStorage.getItem('todos') || '[]')
ngDoCheck(){
window.localStorage.setItem('todos', JSON.stringify(this.todos))
}