文章目录
todos案例
1 提供的数据和 HTML结构
- 引入todos的CSS样式 (HTML和 CSS 已经写好 我们需要改成Vue动态渲染的)
<!-- HTML -->
<section id="todoapp" class="todoapp">
<header class="header">
<h1>todos</h1>
<input placeholder="What needs to be done?" class="new-todo">
</header>
<section class="main">
<input id="toggle-all" type="checkbox" class="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li class="">
<div class="view"><input type="checkbox" class="toggle">
<label>吃饭</label>
<button class="destroy"></button>
</div>
<input class="edit">
</li>
<li class="">
<div class="view">
<input type="checkbox" class="toggle">
<label>睡觉</label>
<button class="destroy"></button>
</div> <input class="edit"></li>
<li class="completed">
<div class="view">
<input type="checkbox" class="toggle">
<label>打豆豆</label>
<button class="destroy">
</button></div>
<input class="edit">
</li>
</ul>
</section>
<footer class="footer">
<span class="todo-count">
<strong>2</strong> item left</span>
<ul class="filters">
<li><a href="#/" class="selected">All</a></li>
<li><a href="#/active">Active</a></li>
<li><a href="#/completed">Completed</a></li>
</ul>
<button class="clear-completed">Clear completed</button>
</footer>
</section>
<!--- 2CSS -->
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/index.css">
<!--- 3、 提供的数据 -->
<script>
new Vue({
el: "#todoapp",
data: {
todos: [{
id: 1,
title: '吃饭',
completed: false
}, {
id: 2,
title: '睡觉',
completed: false
}, {
id: 3,
title: '打豆豆',
completed: true
}]
}
})
</script>
2 把数据渲染到页面上
- 根据completeed 的状态动态给li 绑定类名
- 未完成状态:不需要样式 完成状态: 类名为 completed 编辑状态:类名为 editing
- 如果completed 为 true 则给当前li 添加 completed
<li v-for="(item, index) in todos"
v-bind:class="{completed: item.completed}"
>
<div class="view">
<input type="checkbox" class="toggle">
<label>{{item.title}}</label> <button class="destroy"></button>
</div>
<input class="edit">
</li>
3 把类名是 new-todo 按回车键的时候 把输入框中的数据展示到页面上
-
- 获取文本框中用户输入的数据
-
- 判断数据是否非空 如果是空的,则什么都不做 如果不是空的,则添加到数组中
-
- 添加到数组中
-
- 清空文本框
<header class="header">
<h1>todos</h1>
<!--
@keydown="addTodo" 麻烦,还需要自己来判断 keyCode
@keyup.13="addTodo" 按键修饰符
@keyup.enter="addTodo" 修饰符别名,推荐用法
-->
<input class="new-todo" placeholder="What needs to be done?" @keyup.enter="addTodo" >
</header>
<script>
new Vue({
el: "#todoapp",
data: {
todos: [{
id: 1,
title: '吃饭',
completed: false
}, {
id: 2,
title: '睡觉',
completed: false
}, {
id: 3,
title: '打豆豆',
completed: true
}]
},
methods:{
addTodo(event) {
// 1. 获取文本框中用户输入的数据
var todoText = event.target.value.trim()
// 2. 判断数据是否非空
if (!todoText.length) {
return
}
// 3. 添加到数组中
const lastTodo = this.todos[this.todos.length - 1]
const id = lastTodo ? lastTodo.id + 1 : 1
// 3.1 当数组发生变化,则绑定渲染该数组的视图也会得到更新
this.todos.push({
id,
title: todoText,
completed: false
})
// 4. 清空文本框
event.target.value = ''
},
}
}
})
</script>
4. 实现全选功能
- 4.1 当点击三角即类名为 toggle-all 的复选框的时候
- 如果当前三角高亮 即 复选框为选中状态 让当前所有的li 为 完成状态
- 通过双向绑定获取当前复选框的选中状态
- 否则为未完成状态
- 如果当前三角高亮 即 复选框为选中状态 让当前所有的li 为 完成状态
- 4.2 当点击单个li 里面的复选框的时候 如果当前复选框选中 则当前的状态为完成状态
- 通过双向绑定获取当前复选框选中状态 通过选中状态动态改变 completed 的值
- 4.3 如果当前所有的li 都处于完成状态 即 复选框都选中 则上面的 toggle-all 复选框选中 有一个没有选中则当前toggle-all 复选框 处于未选中状态
<section class="main">
<!-- 4.1 通过双向绑定获取当前复选框的选中状态 --->
<input v-model="toggleStat"
id="toggle-all"
type="checkbox" class="toggle-all">
<ul class="todo-list">
<li v-for="(item, index) in todos"
v-bind:class="{completed: item.completed}">
<div class="view">
<!-- 4.2 当点击单个li 里面的复选框的时候
如果当前复选框选中 则当前的状态为完成状态
4.2.1 通过双向绑定获取当前复选框选中状态
通过选中状态动态改变 completed 的值
--->
<input type="checkbox" class="toggle" v-model="item.completed">
<label>{{item.title}}</label>
<button class="destroy"></button>
</div>
<input class="edit">
</li>
</ul>
</section>
<script>
new Vue({
el: "#todoapp",
methods: {
addTodo(event) {
},
// 删除任务项
removeTodo(delIndex, event) {
this.todos.splice(delIndex, 1)
},
},
computed: {
toggleStat: {
/*
当读取一个变量的时候会触发该变量的getter
当修改该变量时候会触发他的setter.
*/
get() {
// 4.3 4.3 如果当前所有的li 都处于完成状态 即 复选框都选中 则上面的 toggle-all 复选框选中 有一个没有选中则当前toggle-all 复选框 处于未选中状态
return this.todos.every(item => item.completed)
},
// 当复选框为选中的时候当前 传入的为 true 没有选中传入的为false
set(val) {
this.todos.forEach(todo => todo.completed = val)
}
}
}
})
</script>
5 实现删除功能
- 给类名是 destroy 的按钮添加点击事件
- 点击当前按钮 删除当前按钮所在的 li
<section class="main">
<input v-model="toggleStat" id="toggle-all" type="checkbox"
class="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li v-for="(item, index) in todos"
v-bind:class="{completed: item.completed}">
<div class="view">
<input type="checkbox" class="toggle" v-model="item.completed">
<label>{{item.title}}</label>
<!-- 5.1 给按钮按钮添加点击事件 -->
<button class="destroy"
@click="removeTodo(index)">
</button>
</div>
<input class="edit">
</li>
</ul>
</section>
<script>
new Vue({
el: "#todoapp",
methods: {
addTodo(event) {
},
// 删除任务项
removeTodo(delIndex) {
this.todos.splice(delIndex, 1)
},
},
})
</script>
6 实现编辑功能
- 6.1 双击标题的时候 当前li 的类名添加 editing
- 6.1.1 给当前标题添加双击事件
- 6.1.2 给当前li 添加editing 添加editing后 当前隐藏的输入框会显示出来
- 6.2 输入框的默认值为当前标题
- 6.3 当用户没有编辑 的时候 按esc退出的时候 数据不发生变化
- 6.4 当用户输入内容按回车键的时候 把标题更新
- 6.5当用户失去焦点的 时候 把输入框中的标题更新
<section class="main">
<input v-model="toggleStat" id="toggle-all" type="checkbox"
class="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<!--
6.1.1 点击的时候 通过一个标识符 来控制是否给当前li 添加 类名
-->
<li v-for="(item, index) in todos"
v-bind:class="{completed: item.completed, editing: item === currentEditing}">
<div class="view">
<input type="checkbox" class="toggle" v-model="item.completed">
<!--
6.1.1 给当前标题添加双击事件
点击的时候 通过一个标识符 来控制是否给当前li 添加 类名
-->
<label
@dblclick="currentEditing = item"
>{{item.title}}</label>
<button class="destroy"
@click="removeTodo(index)">
</button>
</div>
<!-- 6.2 输入框的默认值为当前标题
6.3 当用户没有编辑 的时候 按esc退出的时候 数据不发生变化
6.4 当用户输入内容按回车键的时候 把标题更新
6.5当用户失去焦点的 时候 把输入框中的标题更新
-->
<input class="edit"
:value="item.title"
@keyup.esc="currentEditing = null"
@keyup.enter="saveEdit(item, index, $event)"
@blur="saveEdit(item, index, $event)"
>
</li>
</ul>
</section>
<script>
new Vue({
el: "#todoapp",
data: {
// 6.1.2 标识符默认的为空即一开始加载的时候类名 editing 不加载
currentEditing: null,
},
methods:{
// 保存编辑项
saveEdit(item, index, event) {
// 1. 拿到文本框中的数据
// 非空校验
// 如果为空,则直接删除这个 item
// 如果不为空,则修改任务项的 title 数据
var editText = event.target.value.trim()
// 程序员要具有工匠精神:优化简写
// !editText.length ?
// this.todos.splice(index, 1) :
// item.title = editText
if (!editText.length) {
// 将元素直接从数组中移除
return this.todos.splice(index, 1)
}
// 2. 将数据设置到任务项中
item.title = editText
// 3. 去除 editing 样式
this.currentEditing = null
},
}
})
</script>
7 Clear completed
- 点击Clear completed 的时候删除所有的 已完成项
<footer class="footer">
<button
class="clear-completed"
@click="removeAllDone">Clear completed</button>
</footer>
<script>
new Vue({
el: "#todoapp",
data: {
// 6.1.2 标识符默认的为空即一开始加载的时候类名 editing 不加载
currentEditing: null,
},
methods:{
// 删除所有已完成任务项
removeAllDone() {
// 找到所有已完成的任务项,把其删除。错误的写法
// this.todos.forEach((item, index) => {
// if (item.completed) {
// // 已完成
// console.log(item.title)
// this.todos.splice(index, 1)
// }
// })
// 把所有需要保留的数据过滤出来,然后重新赋值给 todos
this.todos = this.todos.filter(item => !item.completed)
// 如果想要就在遍历的过程去删除,则可以使用 for 循环
// 没删除一个,我们可以控制让索引 --
// for (let i = 0; i < this.todos.length; i++) {
// if (this.todos[i].completed) {
// this.todos.splice(i, 1)
// i--
// }
// }
},
}
})
</script>
8 number item left
- 通过计算属性检测当前complete未完成的状态
<span class="todo-count"><strong>{{leftCount}}</strong> item left</span>
<script>
new Vue({
computed: {
leftCount: function() {
return this.todos.filter(item => !item.completed).length
}
}
})
</script>
todos案例-组件化抽离
1 提供的数据和 HTML结构
- 引入todos的CSS样式 (HTML和 CSS 已经写好 我们需要改成Vue动态渲染的)
<!-- HTML -->
<section id="todoapp" class="todoapp">
<header class="header">
<h1>todos</h1>
<input placeholder="What needs to be done?" class="new-todo">
</header>
<section class="main">
<input id="toggle-all" type="checkbox" class="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
<li class="">
<div class="view"><input type="checkbox" class="toggle">
<label>吃饭</label>
<button class="destroy"></button>
</div>
<input class="edit">
</li>
<li class="">
<div class="view">
<input type="checkbox" class="toggle">
<label>睡觉</label>
<button class="destroy"></button>
</div> <input class="edit"></li>
<li class="completed">
<div class="view">
<input type="checkbox" class="toggle">
<label>打豆豆</label>
<button class="destroy">
</button></div>
<input class="edit">
</li>
</ul>
</section>
<footer class="footer">
<span class="todo-count">
<strong>2</strong> item left</span>
<ul class="filters">
<li><a href="#/" class="selected">All</a></li>
<li><a href="#/active">Active</a></li>
<li><a href="#/completed">Completed</a></li>
</ul>
<button class="clear-completed">Clear completed</button>
</footer>
</section>
<!--- 2CSS -->
<link rel="stylesheet" href="css/base.css">
<link rel="stylesheet" href="css/index.css">
<!--- 3、 提供的数据 -->
<script>
new Vue({
el: "#todoapp",
data: {
todos: [{
id: 1,
title: '吃饭',
completed: false
}, {
id: 2,
title: '睡觉',
completed: false
}, {
id: 3,
title: '打豆豆',
completed: true
}]
}
})
</script>
2 抽离头部组件
- 把头部封装到一个组件中
- 给输入框绑定事件
- 当用户输入完数据后 通过子向父传值 把获取的用户输入的信息提交到父组件中去
- 父组件接收到子组件传递的数据 存放到todos 中
- 父组件中展示头部组件
<section id="todoapp" class="todoapp">
<!-- 2.2 父组件中展示头部组件
2.3 父组件接收到子组件传递的数据
-->
<myheader @inpvalue="addTodo"></myheader>
</section>
<script>
// 1、把header 部分提取出来
var myheader = {
template: `
<header class="header">
<h1>todos</h1>
<input placeholder="What needs to be done?"
@keyup.enter="addToParent" class="new-todo">
</header>
`,
methods: {
//1.1 把用户输入的数据传递到父组件中去
addToParent(event) {
var todoText = event.target.value.trim()
if (!todoText.length) {
return
}
this.$emit("inpvalue", todoText)
}
}
}
new Vue({
el: "#todoapp",
data: {
currentEditing: null,
todos: [{
id: 1,
title: '吃饭',
completed: false
}, {
id: 2,
title: '睡觉',
completed: false
}, {
id: 3,
title: '打豆豆',
completed: true
}]
},
methods: {
addTodo(todoText) {
# 2.4 父组件接收到子组件传递的数据 存放到todos 中
const lastTodo = this.todos[this.todos.length - 1]
const id = lastTodo ? lastTodo.id + 1 : 1
//当数组发生变化,则绑定渲染该数组的视图也会得到更新
this.todos.push({
id,
title: todoText,
completed: false
})
// 清空文本框
event.target.value = ''
},
}
# 2.1 注册子组件
components: {
myheader
}
})
</script>
3 抽离数据展示组件
- 3.1 把显示数据的代码封装到一个组件中
- 3.2 把父组件中的todos 传递过来 子组件接收到父组件传递过来的数据 进行渲染
- 3.3 点击删除 删除当前的数据
- 3.4 双击的时候 当前数据可编辑
- 3.5 按enter键的时候 保存当前数据
- 3.6 实现全选功能
<section id="todoapp" class="todoapp">
<!-- 3.2.1 父组件通过属性绑定 :todos="todos" 把数据传递给子组件 -->
<!-- 3.3.3 父组件通过 事件监听 @removetodo="removeTodo" 接收子组件传递过来的数据 -->
<!-- 3.4.2 父组件通过 事件监听 @dbl-click="aaaa" 接收子组件传递过来的数据 -->
<mylist :todos="todos" @dbl-click="aaaa"
@removetodo="removeTodo"
@save-edit="saveEdit"
:currentediting="currentEditing"
>
</mylist>
</section>
<script src="js/vue.js"></script>
<script>
var mylist = {
# 3.2.2 子组件通过 props 接收父组件传递过来的数据
props: ["todos", "currentediting"],
template: `
<section class="main">
# 3.6 实现全选功能 通过双向绑定 计算属性 toggleStat1
<input v-model="toggleStat1" id="toggle-all"
type="checkbox" class="toggle-all">
<label for="toggle-all">Mark all as complete</label>
<ul class="todo-list">
# 3.2.3 把todos 展示到当前页面
<li v-for="(item, index) in todos"
v-bind:class="{completed: item.completed,
editing: item === currentediting}">
<div class="view">
<input type="checkbox" class="toggle"
v-model="item.completed">
# 3.4 双击的时候 当前数据可编辑
# 3.4.1 添加双击事件 我们通过 类名 editing 来控制当前输入框显示
# 在父组件中定义一个标识符 currentEditing 默认为 空 当我们双击的时候
# 给当前点击的li 添加 editing 类名
<label @dblclick="doubleMethods(item)">
{{item.title}}</label>
#3.3 点击删除 删除当前的数据
#3.3.1 给按钮绑定事件 需要把当前的id 传入过来
<button class="destroy"
@click="removeTodoParent(index, $event)">
</button>
</div>
# 3.5 按enter键的时候 保存当前数据
<input class="edit"
@keyup.enter="saveEdit(item, index, $event)"
:value="item.title"
@keyup.esc="currentediting = null">
</li>
</ul>
</section>
`,
methods: {
# 3.3.2 删除操作 子组件不要操作父组件里面的数据 把对应的数据传递到父组件中
removeTodoParent(index) {
this.$emit("removetodo", index)
},
# 3.5.1 把当前数据发送到父元素 通过父元素保存子元素数据
saveEdit(item, index, event) {
var editText = event.target.value.trim()
this.$emit("save-edit", editText, item, index)
},
# 3.4.2 子组件 把当前点击的li 传递到父元素
doubleMethods(item) {
this.$emit("dbl-click", item)
}
},
computed: {
# 3.6.1 实现全选功能
toggleStat1: {
get() {
return this.todos.every(item => item.completed)
},
set(val) {
this.todos.forEach(todo => todo.completed = val)
}
},
},
}
new Vue({
el: "#todoapp",
data: {
currentEditing: null,
todos: [{
id: 1,
title: '吃饭',
completed: false
}, {
id: 2,
title: '睡觉',
completed: false
}, {
id: 3,
title: '打豆豆',
completed: true
}]
},
methods: {
// 3.3.4 删除任务项 根据子组件传递过来的 id 删除对应的数据
removeTodo(delIndex) {
this.todos.splice(delIndex, 1)
},
// 保存编辑项
saveEdit(editText, item, index) {
console.log(item, index, '-------------')
// 1. 拿到文本框中的数据
// 非空校验
// 如果为空,则直接删除这个 item
// 如果不为空,则修改任务项的 title 数据
// 程序员要具有工匠精神:优化简写
// !editText.length ?
// this.todos.splice(index, 1) :
// item.title = editText
if (!editText.length) {
// 将元素直接从数组中移除
return this.todos.splice(index, 1)
}
// 2. 将数据设置到任务项中
item.title = editText
// 3. 去除 editing 样式
this.currentEditing = null
},
// 删除所有已完成任务项
removeAllDone() {
this.todos = this.todos.filter(item => !item.completed)
},
# 3.4.3 把当前li的数据 赋值给 currentEditing 即让当前li 绑定 类名
aaaa(item) {
this.currentEditing = item
}
},
computed: {
},
components: {
myheader,
mylist,
}
})
</script>
</body>
</html>
4 抽离fotter 部分
- 4.1 把footer 部分的代码封装到一个组件中
- 4.2 实现 未选中部分的展示
- 4.3 删除已经选中的
<section id="todoapp" class="todoapp">
<myfotter :leftcount="leftCount" @delete-all-done="removeAllDone"></myfotter>
</section>
<script>
# 4.1 把footer 部分的代码封装到一个组件中
var myfotter = {
props: ['leftcount'],
template: `
<footer class="footer">
<span class="todo-count"><strong>{{leftcount}}</strong> item left</span>
<button class="clear-completed" @click="deleteAll">Clear completed</button>
</footer>
`,
methods: {
deleteAll() {
this.$emit("delete-all-done")
}
}
}
new Vue({
computed: {
leftCount: function() {
return this.todos.filter(item => !item.completed).length
},
},
components: {
myheader,
mylist,
myfotter
}
})
</script>
todos接口案例
1 请求后台接口list 获取 数据展示到页面上
- 发送ajax请求
- 在mounted 钩子中将数据渲染到页面上
axios.defaults.baseURL = 'http://localhost:3001/';
new Vue({
data: {
currentEditing: null,
todos: []
},
mounted() {
# 1.2 在mounted 钩子中将数据渲染到页面上
this.getList()
},
methods: {
# 1.1 发送ajax请求
getList() {
axios.get('list').then(res => {
console.log(res.data)
this.todos = res.data
})
}
}
})
2 增加内容
- 在回车事件中发送ajax请求
- 我们已经封装好的 addTodo 方法中发送ajax请求
- 将用户输入的内容添加到后台
- 把用户输入的内容传递给后台
- 因为我们所有的数据增删改查都是在父组件中修改 所有ajax也在父组件的 方法中发送
addTodo(todoText) {
axios.post('add', {
title: todoText,
}).then(res => {
# 在请求成功之后重新渲染页面
this.getList()
})
// 清空文本框
event.target.value = ''
}, event.target.value = '' },
3 双击修改列表信息
- 按回车键或者失去焦点的时候获取到用户信息
- 这里需要把当前列表信息的id获取到
- 把收集到的用户信息发送到后台
## 子组件中 需要把当前id 传递过去
<input class="edit"
@keyup.enter="saveEdit(item, $event)"
@blur="saveEdit(item, $event)"
:value="item.title"
@keyup.esc="currentediting = null">
saveEdit(item, event) {
var editText = event.target.value.trim()
console.log(item, '--------')
this.$emit("save-edit", editText, item)
},
### 父组件方法
### 因为我们所有的数据增删改查都是在父组件中修改 所有ajax也在父组件的 方法中发送
// 保存编辑项
saveEdit(editText, item) {
axios.put(`list/${item.id}`, {
title: editText,
}).then(res => {
this.getList()
// 3. 去除 editing 样式
this.currentEditing = null
})
}
4 删除列表信息
- 点击按钮 删除当前列表信息
- 需要把当前列表的id传递过去
- 发送ajax请求 根据id 删除当前信息
# 4.1 需要把删除按钮的id 传递过去
<button class="destroy" @click="removeTodoParent(item.id, $event)"></button>
# 父组件
### 因为我们所有的数据增删改查都是在父组件中修改 所有ajax也在父组件的 方法中发送
// 删除任务项
removeTodo(delIndex) {
axios.delete(`list/${delIndex}`).then(res => {
this.getList()
})
},
5 删除全部完成
- 在fotter 的Clear completed 的事件处理函数中发送ajax 请求删除所有complete 为true 的
# 父组件
### 因为我们所有的数据增删改查都是在父组件中修改 所有ajax也在父组件的 方法中发送
removeAllDone() {
axios.delete('deleteall').then(res=>
this.getList()
})
6 实现全选功能
- 在toggleStat1 中发送ajax请求 根据当前复选框选中状态 修改后台数据中对应的 complete 值
#2 把数据更新放在父组件中
<mylist @get-list="getList()" ></mylist>
computed: {
toggleStat1: {
get() {
return this.todos.every(item => item.completed)
},
set(val) {
axios.get(`all/${Number(val)}`).then(res => {
#1 子组件通知父组件更新数据
this.$emit('get-list')
})
}
}
}