Javascript Cheatsheet
有许多方法可以解决JavaScript和Odoo中的问题。 但是,Odoo框架被设计为可扩展(这是一个非常大的约束),并且一些常见问题有一个很好的标准解决方案。 标准解决方案可能具有易于理解的odoo开发人员的优势,并且可能会在修改Odoo时继续工作。
本文档试图解释解决其中一些问题的方法。 请注意,这不是参考。 这只是一个随机的菜单集合,或者在某些情况下如何处理的解释。
首先,请记住,使用JS自定义odoo的第一条规则是:尝试在python中执行此操作。 这可能看起来很奇怪,但是python框架是可扩展的,并且可以通过轻触xml或python来完成许多行为。 与使用JS相比,这通常具有更低的维护成本:
如果需要与服务器通信并与javascript框架正确集成,则实现自定义行为通常更加困难。 自定义代码需要复制的框架有许多小细节。 例如,响应,或更新URL,或显示数据而不闪烁。
本文档并未真正解释任何概念。 这更像是一本食谱(cookbook)。 有关更多详细信息,请参阅javascript参考页面(请参阅Javascript参考)
Creating a new field widget
这可能是一个非常常见的用例:我们希望以非常具体(可能依赖于业务)的方式在表单视图中显示一些信息。 例如,假设我们要根据某些业务条件更改文本颜色。
这可以通过三个步骤完成:
- 创建新窗口小部件 widget,
- 在字段注册表中注册它,
- 然后将窗口小部件 widget添加到窗体视图中的字段
- creating a new widget(创建新窗口小部件)
这可以通过扩展小部件来完成:
var FieldChar = require('web.basic_fields').FieldChar;
var CustomFieldChar = Fieldchar.extend({
renderReadonly: function () {
// implement some custom logic here
},
});
- registering it in the field registry(在字段注册表中注册它)
Web客户端需要知道窗口小部件名称与其实际类之间的映射。 这是由注册表完成的:(add
进行注册)
var fieldRegistry = require('web.field_registry');
fieldRegistry.add('my-custom-field', CustomFieldChar);
- adding the widget in the form view(将窗口小部件 添加到窗体视图中的字段)
<field name="somefield" widget="my-custom-field"/>
请注意,只有表单,列表和看板视图使用此字段小部件注册表。 这些视图紧密集成,因为列表和看板视图可以显示在表单视图中。
Modifying an existing field widget
另一个用例是我们想要修改现有的字段小部件。 例如,odoo中的voip插件需要修改FieldPhone小部件,以增加在voip上轻松调用给定数字的可能性。 这是通过包含FieldPhone小部件来完成的,因此无需更改任何现有的表单视图。
Field Widgets(AbstractField的(子类)的实例)与其他所有小部件一样,因此它们可以进行猴子补丁。 这看起来像这样:
var basic_fields = require('web.basic_fields');
var Phone = basic_fields.FieldPhone;
Phone.include({
events: _.extend({}, Phone.prototype.events, {
'click': '_onClick',
}),
_onClick: function (e) {
if (this.mode === 'readonly') {
e.preventDefault();
var phoneNumber = this.value;
// call the number on voip...
}
},
});
请注意,无需将小部件添加到注册表,因为它已经注册。
Modifying a main widget from the interface(从界面修改主窗口小部件)
另一个常见用例是需要从用户界面自定义一些元素。 例如,在主菜单中添加消息。 在这种情况下,通常的过程是再次包含小部件。 这是唯一的方法,因为这些小部件没有注册表。
这通常使用如下代码完成:
var AppSwitcher = require('web_enterprise.AppSwitcher');
AppSwitcher.include({
render: function () {
this._super();
// do something else here...
},
});
Adding a client action
客户端操作是一个小部件,它将控制菜单栏下方的屏幕部分。 如有必要,它可以有一个控制面板。 定义客户端操作可以分两步完成:实现新窗口小部件,并在操作注册表中注册窗口小部件。
- Implementing a new client action:
这是通过创建一个小部件来完成的:
var ControlPanelMixin = require('web.ControlPanelMixin');
var Widget = require('web.Widget');
var ClientAction = Widget.extend(ControlPanelMixin, {
...
});
如果您不需要,请不要添加控制面板mixin。 请注意,需要一些代码才能与控制面板交互(通过mixin给出的update_control_panel
方法)。
- Registering the client action:
像往常一样,我们需要让Web客户端知道客户端操作和实际类之间的映射:
var core = require('web.core');
core.action_registry.add('my-custom-action', ClientAction);
然后,要在Web客户端中使用客户端操作,我们需要使用正确的tag
属性创建客户端操作记录(模型ir.actions.client
的记录):
<record id="my_client_action" model="ir.actions.client">
<field name="name">Some Name</field>
<field name="tag">my-custom-action</field>
</record>
Creating a new view (from scratch)
创建新视图是一个更高级的主题。 这个备忘单(cheatsheet) 只会突出显示可能需要完成的步骤(无特定顺序):
- 将新视图类型添加到
ir.ui.view
的字段类型:
class View(models.Model):
_inherit = 'ir.ui.view'
type = fields.Selection(selection_add=[('map', "Map")])
- 将新视图类型添加到
ir.actions.act_window.view
的字段view_mode
:
class ActWindowView(models.Model):
_inherit = 'ir.actions.act_window.view'
view_mode = fields.Selection(selection_add=[('map', "Map")])
- creating the four main pieces which makes a view (in JavaScript):
我们需要一个视图(AbstractView
的子类,这是工厂),一个渲染器(来自AbstractRenderer
),一个控制器(来自AbstractController
)和一个模型(来自AbstractModel
)。 我建议首先简单地扩展超类:
var AbstractController = require('web.AbstractController');
var AbstractModel = require('web.AbstractModel');
var AbstractRenderer = require('web.AbstractRenderer');
var AbstractView = require('web.AbstractView');
var MapController = AbstractController.extend({});
var MapRenderer = AbstractRenderer.extend({});
var MapModel = AbstractModel.extend({});
var MapView = AbstractView.extend({
config: {
Model: MapModel,
Controller: MapController,
Renderer: MapRenderer,
},
});
- adding the view to the registry:
像往常一样,需要更新视图类型和实际类之间的映射:
var viewRegistry = require('web.view_registry');
viewRegistry.add('map', MapView);
-
implementing the four main classes:
View
类需要解析arch
字段并设置其他三个类。Renderer
负责在用户界面中表示数据,Model
应该与服务器通信,加载数据并处理它。Controller
在那里协调,与网络客户交谈,...... -
creating some views in the database:
<record id="customer_map_view" model="ir.ui.view">
<field name="name">customer.map.view</field>
<field name="model">res.partner</field>
<field name="arch" type="xml">
<map latitude="partner_latitude" longitude="partner_longitude">
<field name="name"/>
</map>
</field>
</record>
Customizing an existing view
假设我们需要创建通用视图的自定义版本。 例如,一个看板视图,顶部有一些额外的带状(ribbon-like )小部件(显示一些特定的自定义信息)。 在这种情况下,这可以通过3个步骤完成: 1.扩展看板视图(也可能意味着扩展控制器/渲染器和/或模型),
2.然后在视图注册表中注册视图,
最后,3.使用看板arch中的视图 (具体示例是帮助台仪表板)。
- extending a view:
这是它的样子:
var HelpdeskDashboardRenderer = KanbanRenderer.extend({
...
});
var HelpdeskDashboardModel = KanbanModel.extend({
...
});
var HelpdeskDashboardController = KanbanController.extend({
...
});
var HelpdeskDashboardView = KanbanView.extend({
config: _.extend({}, KanbanView.prototype.config, {
Model: HelpdeskDashboardModel,
Renderer: HelpdeskDashboardRenderer,
Controller: HelpdeskDashboardController,
}),
});
- adding it to the view registry:
像往常一样,我们需要通知Web客户端视图名称和实际类之间的映射。
var viewRegistry = require('web.view_registry');
viewRegistry.add('helpdesk_dashboard', HelpdeskDashboardView);
- using it in an actual view:
我们现在需要通知Web客户端特定的ir.ui.view
需要使用我们的新类。 请注意,这是Web客户端特定的问题。 从服务器的角度来看,我们仍然有看板视图。 执行此操作的正确方法是在arch的根节点上使用特殊属性js_class
(将在某天重命名为widget
,因为这实际上不是一个好名称):
<record id="helpdesk_team_view_kanban" model="ir.ui.view" >
...
<field name="arch" type="xml">
<kanban js_class="helpdesk_dashboard">
...
</kanban>
</field>
</record>
注意:您可以更改视图解释arch结构的方式。 但是,从服务器的角度来看,这仍然是相同基本类型的视图,受到相同的规则(例如,rng验证)。 因此,您的视图仍需要具有有效的arch字段。