大家好,
在上一章节的教程中,我们讲了Odoo Widget 【挂件】的基础。包括,基础方法和他们的用途。这一节,我们要做的事情,是学会如何应用这些方法到实际使用当中。
首先,我们都知道在编写odoo 的视图xml时,通过在字段中,添加 widget 标签属性即可渲染出相对应的视图。
例如: 时间挂件 ,可以将时间字段渲染成如图所示的效果。
<field name="myfield" widget="timedate"/>
那我们就来具体阐述一下,这个时间挂件所实现的机制和代码编写逻辑。
# 步骤一:
我们需要新建一个模块,命名它为timepicker。文件结构目录如下:【可以用scaffold先创建一个骨架,再进行修改】
# 步骤二:
修改 _manifest_.py,添加相关依赖。
# 步骤三:
我们来看看widget.js
同所有 widget 一样,第一步定义:
odoo.define('web_widget_timepicker', function (require) {
"use strict";
var core = require('web.core');
//web.core是引用web包的core包
var formats = require('web.formats');
//引用web 包中的formats,odoo的js中预制了一部分formats,提供给大家使用
var common = require('web.form_common');
//引用web 包中的common类,一些基础方法的引用
# 步骤四:定义widgets:
varTimeDateField = common.AbstractField.extend(common.ReinitializeFieldMixin, {
//这里的ReinitializeFieldMixin 是最基础的挂件Mixin【其实就是类,js版本的类换汤不换药】
is_field_number: true,
//不做解释,一看就懂
template: "TimeDateField",
// 调用TimeDateField ,它被定义于
internal_format: 'float_time',
//不做解释,一看就懂
widget_class: 'o_form_field_time',
//此项,在模块中的/src/css里面定义
events: {
'change input': 'store_dom_value', // store_dom_value 被定义在下方 },
//事件,是事件选择器(事件名称和由空间分隔的CSS选择器)映射到回调的方法上。回调既可以是控件中的方法名,也可以是函数。一般是指在发生事件的时候,会触发动作。
// 常见的事件还有 'click p.oe_some_class a': 'some_method',
init: function (field_manager, node) {
this._super(field_manager, node);
// field_manager是用来抓取用户输入值的方法,被定义在web.core里面。因为我们的挂件会修改此方法,将输入从原始的录入变为选填。
this.internal_set_value(0);
//如果,没有值,则设置为0
this.options = _.defaults(this.options, {
step: 15,
selectOnBlur: true,
timeFormat: 'H:i',
scrollDefault: 'now',
}); },
// 一些基本设置,提交给jQuery用的,在initialize_content 中被调用
initialize_content: function() {
if(!this.get("effective_readonly")) {
this.$el.find('input').timepicker(this.options);
this.setupFocus(this.$('input'));
}
},
// 初始化内容,对本实例中的el(元素)执行.timepicker方法,并带入options
is_syntax_valid: function() {
if (!this.get("effective_readonly") && this.$("input").size() > 0) {
try {
this.parse_value(this.$('input').val(), '');
return true;
} catch(e) {
return false;
}
}
return true;
},
is_false: function() {
return this.get('value') === '' || this._super();
},
//检查,是否有填写错误
focus: function() {
var input = this.$('input:first')[0];
return input ? input.focus() : false;
},
//焦点事件,只有当点击为焦点后才执行
set_dimensions: function (height, width) {
this._super(height, width);
this.$('input').css({
height: height,
width: width
});
},
//设置输入框的宽度和高度
store_dom_value: function () {
if (!this.get('effective_readonly')) {
this.internal_set_value(
this.parse_value(
this.$('input').val(), ''));
}
},
//保存用户在前端所选择的数据至数据库中
parse_value: function(val, def) {
return formats.parse_value(val, {"widget": this.internal_format}, def);
},
//解析填写进入的数据,在store_dom_value前会调用此方法,以免存的数据格式错误或者其他问题
format_value: function(val, def) {
return formats.format_value(val, {"widget": this.internal_format}, def);
},
//重新格式化内部出现的数据,在render_value 渲染数据前,需要先调用此方法,避免
render_value: function() {
var show_value = this.format_value(this.get('value'), '');
if (!this.get("effective_readonly")) {
this.$input = this.$el.find('input');
this.$input.val(show_value);
} else {
this.$(".o_form_time_content").text(show_value);
}
},
});
//渲染最终数据
core.form_widget_registry.add('timepicker', TimeDateField);
// 将'timepicker' 注册到 widget_registry中(挂件注册表)
return {
TimeDateField: TimeDateField,
};