odoo javascript参考(三)

Widget 指南
id属性在程序或模块中应尽量避免使用,id限制了组件的可重用性,并往往使代码更脆弱.
如果确实需要,(如第三方类需要),id的设置如下:

this.id = _.uniqueId('my-widget-');

避免使用像“content” 或 “navigation”的类名,因为大家都有可能这样用,最好的办法是前面加个前缀,如jzj-xxxx
应避免使用全局选择器。因为一个组件可能在一个页面中使用多次(Odoo中的一个示例是仪表板),查询应限制在给定组件的范围内,
未过滤的选择,如 (选择器)或文档。 q u e r y S e l e c t o r A l l (选择器)通常会导致意外或错误的行为 . O d o o W e b ’ s W i d g e t ( ) 有一个属性, D O M 根节点 ( (选择器)或文档。querySelectorAll(选择器)通常会导致意外或错误的行为. Odoo Web’s Widget()有一个属性, DOM 根节点 ( (选择器)或文档。querySelectorAll(选择器)通常会导致意外或错误的行为.OdooWebsWidget()有一个属性,DOM根节点(el), 还有一个快速节点选择器 ( ( ) ) . 不要假设您的组件拥有或控制超出 ()). 不要假设您的组件拥有或控制超出 ()).不要假设您的组件拥有或控制超出el的任何东西(因此,避免使用对父窗口widget的引用)
Html模板/呈现应该使用QWeb.
所有交互组件(向屏幕显示信息或拦截DOM事件的组件)必须继承自Widget(),并正确实现和使用其API和生命周期
确保在使用$el之前等待start完成,例如:

var Widget = require('web.Widget');

var AlmostCorrectWidget = Widget.extend({
    start: function () {
        this.$el.hasClass(....) // in theory, $el is already set, but you don't know what the parent will do with it, better call super first
        return this._super.apply(arguments);
    },
});

var IncorrectWidget = Widget.extend({
    start: function () {
        this._super.apply(arguments); // the parent promise is lost, nobody will wait for the start of this widget
        this.$el.hasClass(....)
    },
});

var CorrectWidget = Widget.extend({
    start: function () {
        var self = this;
        return this._super.apply(arguments).then(function() {
            self.$el.hasClass(....) // this works, no promise is lost and the code executes in a controlled order: first super, then our code.
        });
    },
});

QWeb Template 引擎
web client使用QWeb Template 引擎渲染widget(),除非它们覆盖渲染方法以执行某些操作。
Qweb JS模板引擎基于XML,与python实现基本兼容。
那么,templates是怎样被加载的呢?. 每当web client启动时, 一个rpc被指向 /web/webclient/qweb 路由.
服务将返回一个模块中定义的所有模板列表(来自于manifest中),
web client 将等待加载模板列表,然后再启动其第一个widget。
这种机制对我们的需求非常有效,但有时,我们希望延迟加载模板,想象一下,我们有一个很少使用的widget,也许我们不希望在主文件中加载其模板,目的是使web客户端稍微轻一点。在这种情况下,我们可以使用widget的xmlDependencies键:

var Widget = require('web.Widget');

var Counter = Widget.extend({
    template: 'some.template',
    xmlDependencies: ['/myaddon/path/to/my/file.xml'],

    ...

});

这样,计数器widget将在其willStart方法中加载xmlDependencies文件,因此在执行渲染时template才加载。
Event System
目前,Odoo支持两个事件系统:一是一个简单的系统,允许添加侦听器和触发事件;另一个更完整的系统,也可以使事件“冒泡”。
这两个事件系统都在文件mixins.js中的EventDispatcherMixin中实现。这个mixin包含在Widget类中。
基本的事件系统:
这个事件系统是独创的,它实现了一个简单的总线模式. 有4个主要方法:

on: 在事件上注册侦听器
off: 移除事件上的侦听器
once: 这用于注册只调用一次的侦听器.
trigger: 触发事件。这将导致调用每个侦听器

示例:

var Widget = require('web.Widget');
var Counter = require('myModule.Counter');

var MyWidget = Widget.extend({
    start: function () {
        this.counter = new Counter(this);
        this.counter.on('valuechange', this, this._onValueChange);
        var def = this.counter.appendTo(this.$el);
        return Promise.all([def, this._super.apply(this, arguments)]);
    },
    _onValueChange: function (val) {
        // do something with val
    },
});

// in Counter widget, we need to call the trigger method:

... this.trigger('valuechange', someValue);

注意:不鼓励使用此事件系统,我们计划用扩展事件系统中的trigger_up方法替换每个触发器方法
Extended Event System
自定义事件widget是一个更高级的系统,它模拟DOM事件API。 无论何时一个事件被触发,它将构建组件树,直到构建后或被终止执行。
trigger_up: 这种方法将创建一个小的OdooEvent并在组件树中调度它。注意,它将从触发事件的组件开始
custom_events: 这相当于事件字典,但适用于odoo事件。
OdooEvent 类很简单, 它有三个公共属性, target (widget触发事件), name (事件名称) 和data (有效载荷). 同时它还有两个方法: stopPropagation 和 is_stopped.

可以更新前面的示例以使用自定义事件系统:

var Widget = require('web.Widget');
var Counter = require('myModule.Counter');

var MyWidget = Widget.extend({
    custom_events: {
        valuechange: '_onValueChange'
    },
    start: function () {
        this.counter = new Counter(this);
        var def = this.counter.appendTo(this.$el);
        return Promise.all([def, this._super.apply(this, arguments)]);
    },
    _onValueChange: function(event) {
        // do something with event.data.val
    },
});

// in Counter widget, we need to call the trigger_up method:

... this.trigger_up('valuechange', {value: someValue});

注册
Odoo生态系统中的一个常见需求是从外部扩展/更改基本系统的行为(通过安装应用程序,即不同的模块),例如, 可能需要在某些视图中添加新的widget类型,基于这种情况, ODOO的做法是创建所需的组件,然后将其添加到注册表(注册步骤),以使web客户端的其余部分知道其存在.
注册分为以下几种情况:
字段注册 ( 从 web.field_registry exported)
例如:

var fieldRegistry = require('web.field_registry');
var FieldPad = ...;
fieldRegistry.add('pad', FieldPad);

它将是 AbstractField的子类。
view注册
此注册表包含 Web 客户端(尤其是视图管理器)已知的所有 JS 视图。此注册表的每个值都应该是AbstractView的子类。

action注册
值必须是AbstractAction。

widget之间的通信
组件之间有多种通信方式。

从parent到child
这是一个简单的案例。父widget可以简单地调用其子widget的方法:

this.someWidget.update(someInfo);

从一个widget到它的父级/某个祖先
在这种情况下,小部件的工作只是通知其环境发生了什么事。由于我们不希望小部件具有对其父级的引用(这会将小部件与其父级的实现耦合),因此最好的方法通常是触发一个事件,该事件将通过使用该trigger_up方法在组件树中冒泡:

this.trigger_up('open_record', { record: record, id: id});

此事件将在widget上触发,然后会冒泡并最终被某些上游widget捕获:

var SomeAncestor = Widget.extend({
    custom_events: {
        'open_record': '_onOpenRecord',
    },
    _onOpenRecord: function (event) {
        var record = event.data.record;
        var id = event.data.id;
        // do something with the event.
    },
});

跨组件
跨组件通信可以通过使用BUS总线来实现. 这不是首选的通信形式, 因为它的缺点是使代码更难维护.无论怎样, 它具有解耦组件的优点. 在下面这个场景中, 这只是通过触发和侦听BUS总线上的事件来实现相应功能。

  // in WidgetA
  var core = require('web.core');

  var WidgetA = Widget.extend({
      ...
      start: function () {
          core.bus.on('barcode_scanned', this, this._onBarcodeScanned);
      },
  });

  // in WidgetB
  var WidgetB = Widget.extend({
      ...
      someFunction: function (barcode) {
          core.bus.trigger('barcode_scanned', barcode);
      },
  });

在这个例子中,我们用到了web.core,但这不是必须的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值