惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cisco Talos Blog
Cisco Talos Blog
Martin Fowler
Martin Fowler
A
About on SuperTechFans
H
Help Net Security
F
Full Disclosure
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
IT之家
IT之家
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
阮一峰的网络日志
阮一峰的网络日志
WordPress大学
WordPress大学
H
Heimdal Security Blog
博客园_首页
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
T
Tailwind CSS Blog
Recent Announcements
Recent Announcements
B
Blog RSS Feed
Last Week in AI
Last Week in AI
V2EX - 技术
V2EX - 技术
The Register - Security
The Register - Security
Security Archives - TechRepublic
Security Archives - TechRepublic
G
GRAHAM CLULEY
美团技术团队
S
Securelist
MyScale Blog
MyScale Blog
Vercel News
Vercel News
L
LINUX DO - 最新话题
Hacker News: Ask HN
Hacker News: Ask HN
W
WeLiveSecurity
M
MIT News - Artificial intelligence
宝玉的分享
宝玉的分享
月光博客
月光博客
Attack and Defense Labs
Attack and Defense Labs
大猫的无限游戏
大猫的无限游戏
博客园 - Franky
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Microsoft Security Blog
Microsoft Security Blog
I
InfoQ
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Forbes - Security
Forbes - Security
罗磊的独立博客
O
OpenAI News
AI
AI
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
雷峰网
雷峰网
S
Security @ Cisco Blogs
MongoDB | Blog
MongoDB | Blog
Schneier on Security
Schneier on Security
P
Proofpoint News Feed
Webroot Blog
Webroot Blog
V
Visual Studio Blog

追梦人物的博客

0x05:Merkle Tree & Patricia Trie 0x04:ECDSA - 以太坊设计与实现 LeetCode 105 从前序与中序遍历构造二叉树 迭代算法原理 + 完整证明 0x03:Address - 以太坊设计与实现 0x02:Secp256k1 - 以太坊设计与实现 0x00:专栏开篇 - 以太坊设计与实现 0x01:RLP 编码 - 以太坊设计与实现 Bellman-Ford 算法原理及其在 DeFi 套利中的应用 - 追梦人物的博客 迪杰斯特拉(Dijkstra)最短路径算法原理、实现与证明 - 追梦人物的博客 实战:CEX-DEX 稳定币套利监控程序开发 - 追梦人物的博客 CEX-DEX 稳定币套利模型 - 追梦人物的博客 Uniswap 手续费和协议费机制剖析 - 追梦人物的博客 Uniswap 流动性机制及相关数学原理分析 - 追梦人物的博客 uv 替代 pyenv + pipx + poetry 环境管理实践 Rust 项目从创建到发布 - 追梦人物的博客 VS Code 调试 Python - 追梦人物的博客 比特币跨市场套利的数学模型 - 追梦人物的博客 ERC-20 相关知识点总结 - 追梦人物的博客 Django 老项目如何从 SQLite 迁到 PostgreSQL 自动生成接口文档 - HelloDjango - django REST framework 教程 单元测试 - HelloDjango - django REST framework 教程 限制接口访问频率 - HelloDjango - django REST framework 教程 API 版本管理 - HelloDjango - django REST framework 教程 如何在 Windows 下搭建高效的 django 开发环境 拓展Python Markdown - 追梦人物的博客 加缓存为接口提速 - HelloDjango - django REST framework 教程 基于 drf-haystack 实现文章搜索接口 - HelloDjango 评论接口 - HelloDjango - django REST framework 教程 实现分类、标签、归档日期接口 - HelloDjango - django REST framework 教程 在接口返回Markdown解析后的内容 - HelloDjango - django REST framework 教程 文章详情接口 - HelloDjango - django REST framework 教程 分页 - HelloDjango - django REST framework 教程 使用视图集简化代码 - HelloDjango - django REST framework 教程 用类视图实现首页 API - HelloDjango - django REST framework 教程 实现博客首页文章列表 API - HelloDjango - django REST framework 教程 初始化 RESTful API 风格的博客系统 - HelloDjango django-rest-framework 是什么鬼? - HelloDjango - django REST framework 教程 结束 or 开始? - HelloDjango Coverage.py 统计测试覆盖率 - HelloDjango - Django博客教程(第二版) 单元测试:测试评论应用 - HelloDjango - Django博客教程(第二版) 单元测试:测试 blog 应用 - HelloDjango Django Haystack 全文检索与关键词高亮 - HelloDjango Django 博客实现简单的全文搜索 - HelloDjango - Django博客教程(第二版) 开启 Django 博客的 RSS 功能 - HelloDjango 统计各个分类和标签下的文章数 - HelloDjango - Django博客教程(第二版) 稳定易用的 Django 分页库,完善分页功能 - HelloDjango 通过 Django Pagination 实现简单分页 - HelloDjango 在脚本中使用 ORM:Faker 批量生成测试数据 - HelloDjango Django 官方推荐的姿势:类视图 - HelloDjango - Django博客教程(第二版) Django 使用 union 合并不同模型(Model) 的查询集(QuerySet) 开发博客文章阅读量统计功能 - HelloDjango - Django博客教程(第二版) 使用 Docker 让部署 Django 项目更加轻松 - HelloDjango 使用 Certbot 向 Let's Encrypt 免费申请 HTTPS 证书 - HelloDjango 使用 Fabric 自动化部署 - HelloDjango 博客代码开源啦 - 追梦人物的博客 (赠书)推荐一本django书籍:Django企业开发实战 - 追梦人物的博客 Nginx+Gunicorn+Supervisor 部署 Django 博客应用 - HelloDjango 优化博客功能细节,提升使用体验 - HelloDjango - Django博客教程(第二版) 交流的桥梁:评论功能 - HelloDjango - Django博客教程(第二版) 分类、归档和标签页 - HelloDjango - Django博客教程(第二版) 页面侧边栏:使用自定义模板标签 - HelloDjango - Django博客教程(第二版) 自动生成文章摘要 - HelloDjango - Django博客教程(第二版) Markdown 文章自动生成目录,提升阅读体验 - HelloDjango - Django博客教程(第二版) 让博客支持 Markdown 语法和代码高亮 - HelloDjango 开发博客文章详情页 - HelloDjango - Django博客教程(第二版) 创作后台开启,请开始你的表演 - HelloDjango - Django博客教程(第二版) 博客从“裸奔”到“有皮肤” - HelloDjango - Django博客教程(第二版) Django 的接客之道 - HelloDjango - Django博客教程(第二版) Django 迁移、操作数据库 - HelloDjango - Django博客教程(第二版) 创建 Django 博客的数据库模型 - HelloDjango 开始进入 django 开发之旅 - HelloDjango "空空如也"的博客应用 - HelloDjango - Django博客教程(第二版) 一种自顶而下的Python装饰器设计方法 - 追梦人物的博客 Python提取支付宝和微信支付二维码 - 追梦人物的博客 2018 - 我的学生生涯最后一年回顾 - 追梦人物的博客 批量清除todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 筛选练习参考答案 - Vue 2.x Todo 教程练习参考答案 删除todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 编辑todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 添加todo练习参考答案 - Vue 2.x Todo 教程练习参考答案 标为完成练习参考答案 - Vue 2.x Todo 教程练习参考答案 入门仪式_Hello_Vue练习参考答案 - Vue 2.x Todo 教程练习参考答案 组件化todo应用 - Vue 2.x Todo 教程 批量清除todo - Vue 2.x Todo 教程 本地存储 - Vue 2.x Todo 教程 筛选 - Vue 2.x Todo 教程 还剩多少todo未完成 - Vue 2.x Todo 教程 自定义指令实现自动聚焦 - Vue 2.x Todo 教程 全部标为完成 - Vue 2.x Todo 教程 删除todo - Vue 2.x Todo 教程 添加todo - Vue 2.x Todo 教程 标为完成 - Vue 2.x Todo 教程 UI - Vue 2.x Todo 教程 显示todo列表 - Vue 2.x Todo 教程 入门仪式:Hello Vue - Vue 2.x Todo 教程 在学习django-rest-framework时收集的学习资料推荐 - 追梦人物的博客 区块链理论与应用研究小组成员招募书 - 追梦人物的博客 Python界网红,豆瓣工程师董伟明加了我的QQ后 - 追梦人物的博客 招募Django学习小组项目组核心成员 - 追梦人物的博客
编辑todo - Vue 2.x Todo 教程
2019-01-07 · via 追梦人物的博客

编辑 todo 的功能略微有点复杂,我们一点点来分解。

首先根据之前的分析,Vue 很容易知道我们想要编辑的是哪一个 todo,只要把当前的 todo 传给绑定的方法即可。我们实现的功能是双击当前的 todo 进入编辑状态,我们就可以在后边的输入框编辑这个 todo,因此需要给元素绑定一个双击监听事件。还有一点需要注意,我们有一个取消编辑的功能,假设用户将 todo 编辑到了一半又不想编辑了,想回到原来的 todo 内容,我们该怎么办?为了解决这个问题,我们可以在 data 返回的对象添加一个属性,让它来暂存编辑前的 todo 状态,如果用户取消了编辑,就让已修改的 todo 退回到之前的状态:

var app = new Vue({
        el: '#todo-app',
        data: function () {
            return {
                todos: [],
                newTodoTitle: '',
                editedTodo: null // 用于暂存编辑前的 todo 状态
            }
        },
    })

另一个问题是后面那个编辑框十分烦人,因为无论我们是否在编辑状态,这个框始终出现。应该根据当前的 todo 是否处于编辑状态来决定它的出现与否,只有当用户双击 todo 进入编辑状态时才出现,那么怎么知道这个 todo 是否是处于当前状态呢?

我们之前在 data 返回的对象里增加了一个 editedTodo 属性,并且把它的初始值设为 null,这个属性的用途是用来暂存编辑前的 todo 状态的,即当用户编辑某个 todo 时,这个 editedTodo 就会被设置为当前 todo 未被编辑前的值。换句换说,如果 editedTodo 为 null,则一定说明这个 todo 不在编辑状态。所以我们可以根据 editedTodo 的值来判断。

<ul>
    <li v-for='todo in todos' :key='todo.id'>
        ...
        <input type="text" 
               value="编辑 todo..."
               v-if="editedTodo !==null"/>
    </li>
</ul>

然后我们为双击 todo 添加一个 editTodo 方法,这个方法把编辑前的 todo 状态暂存到 editedTodo。双击事件为 dblclick:

<li v-for='todo in todos' :key='todo.id'>
      <span :class="{finished: todo.finished}" 
            @dblclick="editTodo(todo)">{{ todo.title }}</span>
      ...
</li>

<script>
    let id = 0; // 用于 id 生成
    var app = new Vue({
        ...
        methods: {
            ...
            editTodo: function (todo) {
                this.editedTodo = {id: todo.id, title: todo.title}
            }
        },
    })
</script>

特别注意这里我们使用了

this.editedTodo = {id: todo.id, title: todo.title, finished: todo.finished}

而不是简单的 this.editedTodo=todo 进行复制,因为这样做的话仅仅只是将对 todo 的引用存到 this.editedTodo,这样的话任何对 todo 的修改都会反映到 editedTodo 上,为了防止这种情况,我们要为 editedTodo 创建一个全新的对象。

打开浏览器刷新,多创建几条 todo(一定要多创建几条),然后双击 todo 的标题,你会发现...

好吧,这并不符合我们的预期,我们希望双击哪条todo,哪条 todo 对应的编辑框弹出来,而不是所有的都弹出来。仔细分析一下我们的代码,我们根据 this.editedTodo !==null 来决定是否显示编辑框,而当某条 todo 被编辑时,this.editedTodo !==null 不再成立,所以所有编辑框都出现了,怎么解决这个问题呢?

只有在被编辑的 todo 的 id 和被暂存的 editedTodo 的 id 相等时,才表示这条 todo 在编辑,而其它的 todo 的 id 和 editedTodo 的 id 都是不相等的,所以我们可以加一个判断:

<ul>
    <li v-for='todo in todos' :key='todo.id'>
        <span :class="{finished: todo.finished}">{{ todo.title }}</span>
        ...
        <input type="button" value="删除" @click="removeTodo">
        <input type="text" value="编辑 todo..."
               v-if="editedTodo!==null && editedTodo.id===todo.id"/>
    </li>
</ul>

要注意 Vue 允许我们在指令中写入任何合法的 javascript 表达式,Vue 会自动对其求值。

然后我们将编辑框的值和 todo 的 title 值双向绑定,那么 todo 的 title 就会跟着编辑框输入的值来变化了,我们也不用担心用户改变了 todo 的 title 值,因为我们已经把编辑前的 todo 的状态暂存到了 editedTodo,想反悔可以随时还原。表单绑定用 v-model:

<li v-for='todo in todos' :key='todo.id'>
    ...
    <input type="text" value="编辑 todo..."
           v-if="editedTodo!==null && editedTodo.id===todo.id"
           v-model="todo.title"/>
</li>

然后就是用户敲击回车,编辑完成,这里我们给编辑框绑定了一个 keyup 方法监听键盘事件,enter 是修饰符,表示这个键盘事件是按下回车,此时会调用 editDone 方法。用户按下回车后因为 todo.title 的值本身就是随着编辑框输入的值变化的,所以我们基本不用做什么事情,如果用户编辑已经完成,暂存的 todo 就不再需要了,我们可以简单地把 editedTodo 还原成 null,这样编辑框的 v-if 判断就会失效,编辑框自动隐藏,完美!

<li v-for='todo in todos' :key='todo.id'>
    ...
    <input type="text" value="编辑 todo..."
           v-if="editedTodo!==null && editedTodo.id===todo.id"
           v-model="todo.title"
           @keyup.enter="editDone(todo)"/>
</li>

<script>
    let id = 0; // 用于 id 生成
    var app = new Vue({
        ...
        methods: {
            ...
            editDone: function (todo) {
                this.editedTodo = null
            }
        },
    })
</script>

取消怎么办呢?取消就是把已经编辑修改的 todo 标题还原,我们的原始信息存在 editedTodo,取出来即可。用户按键盘的 ESC 键进行取消编辑,为此绑定一个键盘事件,和 enter 类似,用 esc 修饰该事件,表示按下的是 ESC 键,然后调用 cancelEdit 方法,该方法将 todo 还原成编辑前的状态:

<li v-for='todo in todos' :key='todo.id'>
    ...
    <input type="text" value="编辑 todo..."
           v-if="editedTodo!==null && editedTodo.id===todo.id"
           v-model="todo.title"
           @keyup.enter="editDone(todo)"
           @keyup.esc="cancelEdit(todo)"/>
</li>

<script>
    let id = 0; // 用于 id 生成
    var app = new Vue({
        ...
        methods: {
            ...
            cancelEdit: function (todo) {
                todo.title = this.editedTodo.title;
                this.editedTodo = null
            }
        },
    })
</script>

注意要编辑框聚焦后按 ESC 才有效。

同样因为用户编辑已经取消,todo 状态已经还原,暂存的 todo 也不再需要了,我们可以简单地把 editedTodo 还原成 null,这样 v-if 判断就会失效,编辑框自动隐藏。

练习

我们应用的体验有一点点不好的地方,如果用户把编辑的内容清空然后按回车确认修改,这时一条空的 todo 就保存了。我们认为用户清空内容就是不想要这条 todo 了,毕竟现实一条空的 todo 没有意义,所以我们应该删除掉这条 todo,实现这个需求。(hint:我们之前实现了 removeTodo 方法,就是做这个的。学会复用代码而不是重复造轮子。)

另外一个不爽的地方就是双击 todo 后弹出编辑框,焦点并没有自动转到编辑框,只有手动点击编辑哭后我们才能编辑内容。这个需求的实现设计到 Vue 的自定义指令功能,我们在下一节来实现。

-- EOF --