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. 基于Vue的单页面应用的Markdown渲染

    之前渲染 Markdown 的时候, 笔者使用的是 mavonEditor 的预览模式, 使用起来比较爽, 只需要引入组件即可, 但是在最近的开发中, 遇到了困难. 主要问题在于作为单页面应用, 站内 ...

  2. 注意力(Attention)与Seq2Seq的区别

    什么是注意力(Attention)? 注意力机制可看作模糊记忆的一种形式.记忆由模型的隐藏状态组成,模型选择从记忆中检索内容.深入了解注意力之前,先简要回顾Seq2Seq模型.传统的机器翻译主要基于S ...

  3. bochs 调试 com 文件 magicbreak

    参考 https://blog.csdn.net/housansan/article/details/41833581 在网上看到2中解决此问题的方法:1.使用dos下的debug32工具单步跟踪pm ...

  4. Leetcode(145)-二叉树的后序遍历

    给定一个二叉树,返回它的 后序 遍历. 示例: 输入: [1,null,2,3] 1 \ 2 / 3 输出: [3,2,1] 思路:一开始编写二叉树后序遍历的程序,感觉定级为困难有点欠妥,确实,如果用 ...

  5. acm 快速傅里叶变换的理解

    A(x)=A4[0](x*x)+x*A4[1](x*x);x=1,w,w*w,w*w*wwi means w^in=4;w=w[4]result shuould bey[0]=A4[0](1*1)+1 ...

  6. 洛谷p1725 露琪诺 单调队列优化的DP

    #include <iostream> #include <cstdio> #include <cstring> using namespace std; int ...

  7. const,volatile,static,typdef,几个关键字辨析和理解

    1.const类型修饰符 const它限定一个变量初始化后就不允许被改变的修饰符.使用const在一定程度上可以提高程序的安全性和可靠性.它即有预编译命令的优点也有预编译没有的优点.const修饰的变 ...

  8. Pygame 游戏开发 All In One

    Pygame 游戏开发 All In One Pygame Pygame is a library for digital arts, games, music, making, and a comm ...

  9. Google coding Style Guide : Google 编码风格/代码风格 手册/指南

    1 1 1 https://github.com/google/styleguide Google 编码风格/代码风格 手册/指南 Style guides for Google-originated ...

  10. 使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包

    使用 js 和 Beacon API 实现一个简易版的前端埋点监控 npm 包 前端监控,埋点,数据收集,性能监控 Beacon API https://caniuse.com/beacon 优点,请 ...