Alan Hou的个人博客

Odoo 14前端框架OWL之创建一个待办清单TodoList应用

完整目录请见Odoo 14全新前端框架 OWL(Odoo Web Library)官方文档中文版

🦉 OWL教程:待办清单TodoApp 🦉

本教程中,我们将创建一个简单的待办清单应用。该应用需满足以下要求:

通过本项目有机会发现并学习Owl的一些重要概念,比如组件、存储以及如何组织应用。

内容

  1. 项目配置
  2. 添加第一个组件
  3. 展示任务清单
  4. 布局:一些基础css
  5. 将任务提取为子组件
  6. 添加任务(第一部分)
  7. 添加任务(第二部分)
  8. 切换任务状态
  9. 删除任务
  10. 使用存储
  11. 在本地存储中保存任务
  12. 过滤任务
  13. 最后的修饰
  14. 最终代码

项目配置

本教程中,我们创建一个非常简单的项目,包含静态文件但没有其它工具。第一步是创建如下的文件结构:

应用的入口是index.html,内容如下:

当前app.css可留空。稍后用于对应用添加样式。在app.js中编写主代码。现在使用如下代码:

注意这里把所有代码放到了立即执行的函数中,来避免对全局产生任何影响。

最后应当从Owl仓库下载最新版的owl.js(你也可以直接使用owl.min.js)。

访问https://github.com/odoo/owl/releases下载最新版本 owl.js

此时,项目准备就绪。在浏览器中加载index.html文件,页面内容为空,标题为Owl Todo App,在控制台中会打印出hello owl 1.0.0这样的消息。

添加第一个组件

Owl应用由组件构成,包含一个根组件。下面我们来定义一个App组件。使用如下代码替换掉app.js 中函数的内容。

此时重新加载页面会显示一条消息。

代码非常简单,但我们来详细讲解一下最后一行代码。浏览器会尝试尽快执行app.js中的JS代码,在有可能在加载App组件时DOM尚未准备就绪。为避免这一问题,我们使用whenReady帮助函数来将setup函数的执行延迟至DOM准备就绪后。

注意1:在更大型的项目中,我们会把代码分割成多个文件,在子文件夹中放置组件,主文件会用于初始化应用。但这里是一个非常小的项目,我们希望保持尽量简单。

注意2:本教程中使用了静态类字段语法。有些浏览器尚不支持。大部分真实项目会对代码进行转译,不会存在问题,但要让本教程的代码在各个浏览器中正常使用,需要将每个static关键词转换为类赋值:

注意3:使用xml helper 行内模板很好,但没有语法高亮,这会很容易写出格式错误的xml。针对这一情况有些编辑器支持语法高亮。例如,VS Code中有一个插件Comment tagged template,安装后会正常显示带标签模板:

注意4:大型应用可能需要对模板进行翻译。使用行内模板会让其变得困难,因为我们需要额外的工具类从代码中提取 xml,再使用翻译值进行替换。

展示任务清单

现在已完成基础工作。是时候考虑任务这块了。为完成所需,我们要使用如下键的对象数组记录任务。

既然决定好了状态的内部格式,可以对App组件添加一些演示数据及模板:

模板中包含一个t-foreach循环对任务进行遍历。它可丰组件中查找任务列表,因为组件是渲染上下文。注意我们将每个任务的id作为t-key,这是常见做法。有两个css类:task-list 和 task,在下一节中进行使用。

最后,注意t-att-checked属性的使用:在属性名前添加t-att来让其成为动态属性。Owl将会运行表达式并设置结果为属性值。

布局:一些基础css

至此,我们的任务清单还不好看。在app.css中添加如下样式:

这样好多了。下面添加一些其它功能:完成的任务样式应当有不同,来显示其重要程度较低。这时可以对每个任务添加一个动态css类:

注意在这里我们再次使用到了动态属性。

将任务提取为子组件

现在很清晰应当有一个Task组件用于封装任务的外观和行为。

Task组件会展示傻,但不能拥有任务的状态:一段数据仅能有一个所有者。否则会是自找麻烦。因此Task组件会以prop获取其数据。这表示数据仍由App组件所持有,但可由Task组件使用(不进行修改)。

因为我们在动代码,趁机可以对代码进行一些重构:

这背后发生了很多事:

添加任务(第一部分)

我们还在使用硬编码的任务。是时候让用户可以自己添加任务了。第一步是对App组件添加一个输入框。这个输入框位于任务清单之外,因此需要调整App的模板、JS 和 CSS(注意其中对 task-list 样式的修改):

现在输入框可以使用了,它会在用户每添加一个任务时在控制台中进行记录。注意在加载页面时,光标不会聚焦到输入框。但添加任务是任务清单的核心功能,我们对输入框进行聚焦来让这一操作更快速。

因为App是一个组件,它存在一个mounted生命周期方法可供实现。我们还需要获取对输入框的引用,可通过带有useRef钩子的t-ref指令。

inputRef以类字段进行定义,因此和在构造函数中进行定义是一样的。它告知Owl使用对应的t-ref关键词来引用元素。然后我们实现了mounted生命周期方法,对当前引用的输入框进行聚焦显示。

添加任务(第二部分)

在前一部分中,我们尚未实现任务创建部分的代码。下面进行编写。

需要有一种方式来生成id数字。这只需在App中添加一个nextId数字。同时,我们从App中删除演示任务:

这时可以开始实现addTask方法:

这基本上可以了,但如果测试的话,会注意到在用户输入Enter时不会显示新任务。但如果添加了debuggerconsole.log语句的话,会看到代码实际上正常运行了。问题在于Owl不知道它需要重新渲染用户界面。可以使用useState钩子来将tasks变为响应式解决这一问题:

这时就可以正常使用了。

切换任务状态

如果尝试将任务标记为完成,可能会注意到透明度没有任何变化。这是因为还没有写修改isCompleted标记的代码。

这里有一个有趣的状况:任务由Task组件显示,但它却不是状态的所有者,因而无法修改状态。我们转而希望通过通讯请求来切换App组件任务的状态。因AppTask的父组件,可以在Task触发一个事件并在App中监听该事件。

Task中,修改input如下:

并添加toggleTask方法:

下面我们需要在App模板中监听该事件:

并实现toggleTask的代码:

删除任务

下面添加删除任务的功能。首先对每个任务添加一个垃圾筒图标,然后像前面小节中一样逐步修改。

第一部,更新Task模板、css和js:

现在,我们需要监听App中的delete-task事件:

使用存储(store)

查看代码,很明显处理任务的代码散落在不止一个地方。同时UI和业务逻辑代码混在一起。Owl有一种通过用户界面管理状态的方式:Store.。

我们在应用中进行使用。这对我们的应用是一个比较大的重构,因为涉及到从组件提取出所有任务相关代码。下面是app.js文件的新内容:

在本地存储中保存任务

现在我们的TodoApp似乎使用正常,但用户关闭或刷新浏览器后就现原形了!仅在内存中保存应用状态真的是很不便。要解决这一问题,可以把任务保存到本地存储中。对当前的代码只需做简单的修改:仅setup代码需要做更新。

关键点是存储是一种EventBus,在更新时会触发update事件。

过滤任务

我们差不多已经完成了,可以添加、更新、删除任务。唯一还少的功能是根据完成状态显示任务。我们将在App中记录过滤的状态,然后根据其值过滤出可见任务。

最后,我们需要显示可见的过滤器。可以实现的同时在主列表下方以小面板显示任务数量:

注意这里我们使用对象语法动态地设置了过滤器class:如果键和值匹配则是需要显示 class 的。

最后的修饰

我们的列表功能已完成。还可以再添加一些细节来改善用户体验。

  1. 在用户鼠标悬浮到任务上方时添加视觉反馈:
  2. 可点击任务标题来切换复选框状态:
  3. 对已完成任务的标题添加中划线:

最终代码

至此就完成了我们的应用。功能实现,UI代码与业务逻辑进行了很好的分离,可进行测试,代码均小于150行(包含模板)。

为方便读者参考,以下是最终的代码:

 

 

退出移动版