odoo14引入了名为OWL(Odoo Web Library)的JavaScript框架。OWL是以组件为基础的UI框架,通过QWeb模板作为架构。OWL与传统的组件系统相比更快,并引入了一些新的特性,包括hooks、reactivity、the autoinstantiation of subcomponents等。在这章中,我们将学习如何使用OWL创建可交互的UI元素。我们将从最小的OWL组件开始,然后学习组件的生命周期。最后,我们将创建一个新的form视图下的字段控件。本章将包含如下内容:

  • 创建OWL组件
  • 在OWL组件中管理用户动作
  • TODO: Making OWL components reactive
  • 理解OWL的生命周期
  • 向form视图中添加OWL字段

注意

为什么odoo不使用一些比较知名的JavaScript框架,比如React.js、Vue.js呢?你可以在https://github.com/odoo/owl了解到OWL的更多知识。

技术要求

OWL组件是以ES6定义的。在这章中,我们将使用ES6语法。但是一些ES6语法在一些老的浏览器中有问题。请确保使用最新的Chrome或者Firefox浏览器。

创建OWL组件

本节的目标是学习OWL组件的基础知识。我们将创建最小的OWL组件并把它添加到Odoo的Web客户端中。

本节,我们会创建一个小的带有文字的水平条。

准备

本节,我们将使用my_library模块。

步骤

我们将添加一个小的组件,用于展示水平文字的长条。

  1. 添加/my_library/static/src/js/component.js的JavaScript文件并定义新的命名空间:
odoo.define('my.component', function (require) {
"use strict";
// Place steps 3, 4, 5 here
});
  1. 添加/my_library/views/tempaltes.xml的项目xml文件并载入js文件:
<template id="assets_end" inherit_id="web.assets_backend">
<xpath expr="." position="inside">
<script src="/my_library/static/src/js/component.js" type="text/javascript" />
</xpath>
</template>
  1. 在步骤1中创建js文件中新增OWL实用程序
const { Component } = owl;
const { xml } = owl.tags;
  1. 在步骤1中创建的js文件中添加OWL组件及基础的模板:
class MyComponent extends Component {
static template = xml`
<div class="bg-info text-center p-2">
<b> Welcome to Odoo </b>
</div>`
}
  1. 初始化组件并添加到网页客户端:
owl.utils.whenReady().then(() => {
const app = new MyComponent();
app.mount(document.body);
});

安装/更新my_library模块应用更改。当我们的模块完成安装后,我们可以看到水平条。

这只是一个简单的组件。它不能响应用户事件,你也不能移除他。

原理

步骤1、步骤2,我们添加了js文件并将其添加到后台资源(assets)中。如果想学习assets的内容,可参考14章、CMS网站开发、静态资源管理。

步骤3,我们通过OWL初始化了一个变量。所有通过OWL实例化的变量在全局变量owl中都是可见的。在我们的例子中,我们使用了OWL实例。首先,我们定义了Component,然后通过owl.tags定义了xml。对于OWL组件而言,Component是核心类,通过扩展它,我们可以创建我们自己的组件。

步骤4,我们创建了组件,MyComponent。简单起见,我们仅添加了QWeb的模板。如果你观察的比较仔细,可以看到我们使用xml...定义了模板。这就是内联模板(inline template)。然后,你也可以载入QWeb模板。

小贴士

内联QWeb模板并不支持翻译及通过继承进行修改。因此,尽量使用单独的QWeb文件。

步骤5,我们实例化了MyComponent并把它追加到body中。OWL组件是ES6的类,所以你能够通过new创建实体。然后通过mount()函数添加到页面中。我们把我们的代码写在了whenReady()的回调函数中。这可以确保在使用OWL组件前,所有的OWL功能都被加载完成。

更多

OWL在odoo中是单独加载的库,就像其他的JS库一样。你能够使用OWL构建其他的项目。

在OWL组件中管理用户行为

为了确保用户接口具有可交互性,组件需要响应用户的点击、悬停及表格的提交。

在本节中,我们将添加一个按钮并处理点击事件。

准备

步骤

本节,我们将添加删除按钮。通过点击删除按钮,可以移除组件。如下:

  1. 更新QWeb模板并添加icon图标。
static template = xml`
<div class="bg-info text-center p-2">
<b> Welcome to Odoo </b>
<i class="fa fa-close p-1 float-right" style="cursor: pointer;" t-on-click="onRemove"> </i>
</div>`
  1. 添加onRemove处理函数
class MyComponent extends Component {
static template = xml`
<div class="bg-info text-center p-2">
<b> Welcome to Odoo </b>
<i class="fa fa-close p-1 float-right"
style="cursor: pointer;"
t-on-click="onRemove"> </i>
</div>`
onRemove(ev) {
this.destroy();
}
}

更新模块后,视图如下:

点击移除的图标后,组件将被删除。当刷新页面后,水平条将再次出现。

原理

步骤1,我们添加了移除的图标,并且添加了t-on-click属性。这将绑定点击事件。属性的值就是响应方法的名称。在我们的例子中,onRemove是我们的响应函数。组件的事件语法如下

t-on-<name of event>="<method name in component>"

比如,当我们想当鼠标移至组件上时进行响应,则可以

t-on-mouseover="onMouseover"

在添加响应代码后,当我们的鼠标悬停在组件上时,OWL将会调用onMouseover方法。

步骤2,我们添加了onRemove方法。当我们点击移除图标时调用。在这个方法中,我们调用了destory()方法,这将会移除组件。在destory()函数中,我们接收JavaScript事件对象。destory()是OWL组件的默认方法之一。

更多

事件并不局限于DOM事件。你可以添加自己的事件。比如,你触发了名为my-custom-event的方法,你可以使用t-on-my-custom-event捕获事件。

Making OWL 组件reactive

OWL是一个强有力的框架,可根据钩子自动更新UI。有了更新钩子,当组件的内部状态发生变化后,组件的UI可自动更新。在本节中,我们将更新展示在组件UI中的内容。

准备

步骤

本节中,我们在文本两边添加了箭头的图标。通过点击箭头,我们可以改变文本内容。如下:

  1. 更新XML的模板。添加两个绑定事件的按钮。可以从列表中动态检索文本。
static template = xml`
<div class="bg-info text-center p-2">
<i class="fa fa-arrow-left p-1"
style="cursor: pointer;"
t-on-click="onPrevious"> </i>
<b t-esc="messageList[Math.abs(
state.currentIndex%4)]"/>
<i class="fa fa-arrow-right p-1"
style="cursor: pointer;"
t-on-click="onNext"> </i>
<i class="fa fa-close p-1 float-right"
style="cursor: pointer;"
t-on-click="onRemove"> </i>
</div>`
  1. 在JavaScript文件中引入userState钩子:
const { Component, useState } = owl;
  1. 添加constructor方法并初始化一些变量
constructor() {
super(...arguments);
this.messageList = [
'Hello World',
'Welcome to Odoo',
'Odoo is awesome',
'You are awesome too'
];
this.state = useState({ currentIndex: 0 });
}
  1. 在组件类中,添加用户点击事件
onNext(ev) {
this.state.currentIndex++;
}
onPrevious(ev) {
this.state.currentIndex--;
}

更新模块,展示如下:

原理

步骤1,我们更新了XML模板。我们做了两个改动。我们通过消息的列表渲染文本消息,我们基于在state变量中的currentIndex的值选择消息。我们在文本框两边添加了两个箭头。并通过t-on-click属性绑定了点击事件。

步骤2,我们引入了useState钩子。将用于处理组件的状态。

步骤3,我们添加了构造函数(constructor)。当我们创建对象实体时,构造函数将会被调用。在构造函数中,我们添加了消息的列表。然后通过useState钩子新增了state的变量。当state变化的时候,UI也将更新。在我们的例子中,我们在useState钩子中使用了currentIndex。当currentIndex变化了,UI也将随之变化。

重要信息

在定义钩子的时候只有一条规则,只有在构造函数中定义了钩子,钩子才会生效。几个其他钩子可以在https://github.com/odoo/owl/ blob/master/doc/reference/hooks.md详细了解。

步骤4,我们添加了箭头的点击事件。通过点击箭头,我们可以改变组件的状态。因为我们再state上使用了钩子,UI也将随之变化。

理解OWL的生命周期

OWL组件有几个方法帮助开发人员创建强有力的组件。本节,我们将了解组件重要的方法及组件的生命周期。本节,我们添加了几个方法,我们将在console中输出日志以了解组件的生命周期。

准备

步骤

  1. 在构造函数(constructor)中添加日志
constructor() {
console.log('CALLED:> constructor');
...
  1. 添加willStart方法
async willStart() {
console.log('CALLED:> willStart');
}
  1. 添加mounted方法
mounted() {
console.log('CALLED:> mounted');
}
  1. 添加willPatch方法
willPatch() {
console.log('CALLED:> willPatch');
}
  1. 添加patched方法
patched() {
console.log('CALLED:> patched');
}
  1. 添加willUnmount()方法
willUnmount() {
console.log('CALLED:> willUnmount');
}

更新模块后如下图:

原理

constructor(): 构造函数,最先被调用。将在这里设置组件的初始状态。

willStart(): 在构造函数之后,渲染元素之前。这是异步函数,可以进行诸如RPC的异步操作。

mounted(): 在元素渲染、DOM添加之后调用。

willPatch(): 在组将的状态发生变化之后调用。这个方法将在元素被根据新的状态重新渲染前调用。例如,当我们点击箭头的时候,该函数被调用。但是这时dom依旧是老的值。

patched(): 与willPatched()类似。在组件的状态发生变化的时候调用。不同点是,函数在元素基于新的状态渲染后调用。

willUnmount(): 在元素被移除前调用。

以上是组件的生命周期,你可以根据实际需要编写相应函数。比如,mounted和willUnmount方法可以用来绑定和解绑事件监听。

更多

还有一个重要的方法,他在你使用子组件的使用调用。OWL传递通过props参数传递父组件的状态给子组件,当props变化的时候,willUpdateProps方法将被调用。这是一个异步方法,意味着你可以进行诸如RPC的异步操作。

为form视图添加OWL字段

至此,我们学习了OWL的基础知识。现在我们创建一个form视图下的字段展示组件。我们将创建一个颜色部件,通过选择颜色保存数值。

为了让例子更丰富,我们使用了OWL的先进理念。我们将创建复杂的组件,用户事件,扩展的QWeb模板等。

准备

步骤

  1. 在library.book模型中添加颜色的整数型字段
color = fields.Integer()
  1. 在form视图中添加相同的字段
<field name="color" widget="int_color"/>
  1. 在static/src/xml/qweb_tempalte.xml中添加字段的QWeb模板
<?xml version="1.0" encoding="UTF-8"?>
<templates>
<t t-name="OWLColorPill" owl="1">
<span t-attf-class="o_color_pill o_color_{{props.pill_no}} {{props.active and'active' or ''}}" t-att-data-val="props.pill_no" t-on-click="pillClicked"t-attf-title="This color is used in {{props.book_count or 0 }} books." />
</t>
<span t-name="OWLFieldColorPills" owl="1" class="o_int_colorpicker" t-on-color-updated="colorUpdated">
<t t-foreach="totalColors" t-as='pill_no'>
<ColorPill t-if="mode === 'edit' or value == pill_no" pill_no='pill_no' active='value == pill_no' book_count="colorGroupData[pill_no]"/>
</t>
</span>
</templates>
  1. 在manifest文件中添加QWeb文件
"qweb": [
'static/src/xml/qweb_template.xml',
],
  1. 现在我们在static/src/scss/field_widget.scss中添加一些SCSS。文件太长了,可直接在https://github.com/ PacktPublishing/Odoo-13-Development-Cookbook-Fourth- Edition/blob/master/Chapter16/05_owl_field/my_library/ static/src/scss/field_widget.scss中查看。
  2. 添加static/src/js/field_widget.js
odoo.define('my_field_widget', function (require) {
"use strict";
const { Component } = owl;
const AbstractField = require(
'web.AbstractFieldOwl');
const fieldRegistry = require(
'web.field_registry_owl');
// Place steps 7 and 8 here
});

步骤7,添加颜色选择组件

class ColorPill extends Component {
static template = 'OWLColorPill';
pillClicked() {
this.trigger('color-updated', {val:
this.props.pill_no});
}
}

步骤8,扩展AbstractField

class FieldColor extends AbstractField {
static supportedFieldTypes = ['integer'];
static template = 'OWLFieldColorPills';
static components = { ColorPill };
// Add methods from step 9 here
}
fieldRegistry.add('int_color', FieldColor);

步骤9,添加方法

constructor(...args) {
super(...args);
this.totalColors = Array.from({ length: 10 },
(_, i) => (i + 1).toString());
}
async willStart() {
this.colorGroupData = {};
var colorData = await this.rpc({
model: this.model, method: 'read_group',
domain: [], fields: ['color'],
groupBy: ['color'],
});
colorData.forEach(res => {
this.colorGroupData[res.color] =
res.color_count;
});
}
colorUpdated(ev) {
this._setValue(ev.detail.val);
}

步骤10,将js、scss文件添加到后台资源。

<template id="assets_backend" inherit_id="web.assets_ backend">
<xpath expr="." position="inside">
<script src="/my_library/static/src/js
/component.js" type="text/javascript" />
<script src="/my_library/static/src/js
/field_widget.js" type="text/javascript" />
<link href="/my_library/static/src/scss
/field_widget.scss" rel="stylesheet" type="text/scss" />
</xpath>
</template>

更新模块,如下图

这个字段看起来就像上一章中的color小部件,但实际的区别在于它的底层。这个新字段是用OWL构建的,而前一个字段是用小部件构建的。

原理

步骤1,我们创建了整型字段。

步骤2,我们添加到form视图。

步骤3,我们添加了QWeb模板。我们添加了两个模板,一个是颜色的选择,另一个是字段本身。我们使用两个模板是为了更好的理解子组件的概念。仔细查看模板,可以发现我们使用了标签。浙江实例化子组件。在标签中,我们传递active和pill_no属性。这些属性的值将通过子组件参数props获取。同时,t-on-color-updated属性被用来监听子组件的自定义事件。

重要信息

odoo14使用widget系统和OWL框架。两个都使用QWeb模板。为了将OWL QWeb模板与传统的QWeb 模板区分,我们需使用owl="1"来标识

步骤4,添加文件到manifest中。

步骤5,添加SCSS的样式。

步骤6,添加JS。我们引入OWL实用程序,并导入AbstractField和fieldRegistry。AbstractField是抽象的OWL组件。他包含所有基础元素。fieldRegistry被用来展示OWL组件。

步骤7,我们创建了ColorPill组件。组件中template变量是从外部XML 文件中加载的模板的名称。ColorPill组件有pillClicked方法,用于用户在颜色上的点击。在方法内部,当我们在FieldColor组件上使用t-on-color-updated的触发的color-updated事件将会被父组件FieldColor组件捕获。

步骤8、9,我们创建了FieldColor组件,它是AbstractField的拓展。我们之所以使用AbstractField组件,是因为它具备创建字段小部件的所有要素。我们再一开始使用了components静态变量。当你在模板中使用子组件的时候,你需要通过components静态变量列出所有的组件。我们添加了willStart方法。willStart方法是异步方法,我们可以调用RPC实现获取特定颜色组图书的数量。然后,我们添加了colorUpdated方法,在我们点击是调用。所以,当我们变化了字段的值时。setValue方法将会设置字段的值。注意,由子组件触发的数据,在event参数下的detail属性中是可以访问到的。最后,我们在fieldRegistry中注册了小部件,这意味着今后我们将能够通过表单视图中的小部件属性使用字段。

在步骤10中,我们将JavaScript和SCSS文件加载到后端资产中。

【odoo14】第十六章、odoo web库(OWL)的更多相关文章

  1. SpringBoot | 第十六章:web应用开发

    前言 前面讲了这么多直接,都没有涉及到前端web和后端交互的部分.因为作者所在公司是采用前后端分离方式进行web项目开发了.所以都是后端提供api接口,前端根据api文档或者服务自行调用的.后台也有读 ...

  2. SpringBoot | 第二十六章:邮件发送

    前言 讲解了日志相关的知识点后.今天来点相对简单的,一般上,我们在开发一些注册功能.发送验证码或者订单服务时,都会通过短信或者邮件的方式通知消费者,注册或者订单的相关信息.而且基本上邮件的内容都是模版 ...

  3. UNP学习笔记(第二十六章 线程)

    线程有时称为轻权进程(lightweight process) 同一进程内的所有线程共享相同的全局内存.这使得线程之间易于共享信息,然后这样也会带来同步的问题 同一进程内的所有线程处理共享全局变量外还 ...

  4. 进击的Python【第十六章】:Web前端基础之jQuery

    进击的Python[第十六章]:Web前端基础之jQuery 一.什么是 jQuery ? jQuery是一个JavaScript函数库. jQuery是一个轻量级的"写的少,做的多&quo ...

  5. 第三百三十六节,web爬虫讲解2—urllib库中使用xpath表达式—BeautifulSoup基础

    第三百三十六节,web爬虫讲解2—urllib库中使用xpath表达式—BeautifulSoup基础 在urllib中,我们一样可以使用xpath表达式进行信息提取,此时,你需要首先安装lxml模块 ...

  6. 第十二章 Odoo 12开发之报表和服务端 QWeb

    报表是业务应用非常有价值的功能,内置的 QWeb 引擎是报表的默认引擎.使用 QWeb 模板设计的报表可生成 HTML 文件并被转化成 PDF.也就是说我们可以很便捷地利用已学习的 QWeb 知识,应 ...

  7. 《Linux命令行与shell脚本编程大全》 第十六章 学习笔记

    第十六章:创建函数 基本的脚本函数 创建函数 1.用function关键字,后面跟函数名 function name { commands } 2.函数名后面跟空圆括号,标明正在定义一个函数 name ...

  8. Gradle 1.12用户指南翻译——第二十六章. War 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  9. Gradle 1.12用户指南翻译——第三十六章. Sonar Runner 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  10. 20190902 On Java8 第十六章 代码校验

    第十六章 代码校验 你永远不能保证你的代码是正确的,你只能证明它是错的. 测试 测试覆盖率的幻觉 测试覆盖率,同样也称为代码覆盖率,度量代码的测试百分比.百分比越高,测试的覆盖率越大. 当分析一个未知 ...

随机推荐

  1. Python——requests模块

    一.安装模块 pip install requests 二.引用 import requests 三.get方法 #GET访问页面 r = requests.get(url) print(r.text ...

  2. Mysql(三)------事务的特性、事务并发、事务读一致性问题

    1 什么是数据库的事务? 1.1 事务的典型场景 在项目里面,什么地方会开启事务,或者配置了事务?无论是在方法上加注解,还 是配置切面 <tx:advice id="txAdvice& ...

  3. 高并发之Semaphore、Exchanger、LockSupport

    本系列研究总结高并发下的几种同步锁的使用以及之间的区别,分别是:ReentrantLock.CountDownLatch.CyclicBarrier.Phaser.ReadWriteLock.Stam ...

  4. React components render order All In One

    React components render order All In One components render order / components lifecycle DOM tree ren ...

  5. TDD & Unit testing

    TDD & Unit testing TDD jest https://github.com/facebook/jest https://facebook.github.io/jest/zh- ...

  6. 2020 新型肺炎病毒疫情 & 远程办公

    2020 新型肺炎病毒疫情 & 远程办公 2020 新型肺炎病毒疫情 https://zhuanlan.zhihu.com/p/104406687 钉钉 微信 code gitlab PRD ...

  7. PAA养老房产:以情怀打造精细化服务

    养老服务工作需要从业者具备尊老.爱老.敬老的职业道德,这种职业道德的培养非一朝一夕可锻造,而是需要长年累月.一点一滴的渗透和养成.PAUL ADAMS ARCHITECT(以下简称PAA)(公司编号: ...

  8. JULLIAN MURPHY:拥有良好的心态,运气福气便会自来

    JULLIAN MURPHY是星盟全球投资公司的基金预审经理,负责星盟投资项目预审,有着资深的基金管理经验,并且在区块链应用的兴起中投资了多个应用区块链技术的公司. JULLIAN MURPHY认为往 ...

  9. BGV上线两天价格超过880美金,下一个YFI已到来!

    BGV自上线以来就备受币圈关注,众多投资者纷纷表示看好BGV.BGV也不负众望,在上线交易所第二天,价格就迎来了暴涨,最高价格为888.88美金,超越了当前以太坊的价值.而这也迎来了币圈众多投资者的一 ...

  10. C++算法代码——笨小猴

    题目来自:http://218.5.5.242:9018/JudgeOnline/problem.php?id=1163 题目描述 笨小猴的词汇量很小,所以每次做英语选择题的时候都很头疼.但是他找到了 ...