[IMP] client actions: allow functions, not just widgets
bzr revid: xmo@openerp.com-20121003114258-vp3mg1yqps4qfkyp
This commit is contained in:
parent
0b0bce6b01
commit
10513dc524
|
@ -614,12 +614,8 @@ instance.web.client_actions.add("login", "instance.web.Login");
|
||||||
* Client action to reload the whole interface.
|
* Client action to reload the whole interface.
|
||||||
* If params has an entry 'menu_id', it opens the given menu entry.
|
* If params has an entry 'menu_id', it opens the given menu entry.
|
||||||
*/
|
*/
|
||||||
instance.web.Reload = instance.web.Widget.extend({
|
instance.web.Reload = function(parent, params) {
|
||||||
init: function(parent, params) {
|
var menu_id = (params && params.menu_id) || false;
|
||||||
this._super(parent);
|
|
||||||
this.menu_id = (params && params.menu_id) || false;
|
|
||||||
},
|
|
||||||
start: function() {
|
|
||||||
var l = window.location;
|
var l = window.location;
|
||||||
|
|
||||||
var sobj = $.deparam(l.search.substr(1));
|
var sobj = $.deparam(l.search.substr(1));
|
||||||
|
@ -627,26 +623,23 @@ instance.web.Reload = instance.web.Widget.extend({
|
||||||
var search = '?' + $.param(sobj);
|
var search = '?' + $.param(sobj);
|
||||||
|
|
||||||
var hash = l.hash;
|
var hash = l.hash;
|
||||||
if (this.menu_id) {
|
if (menu_id) {
|
||||||
hash = "#menu_id=" + this.menu_id;
|
hash = "#menu_id=" + menu_id;
|
||||||
}
|
}
|
||||||
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
|
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
|
||||||
window.location = url;
|
window.location = url;
|
||||||
}
|
};
|
||||||
});
|
|
||||||
instance.web.client_actions.add("reload", "instance.web.Reload");
|
instance.web.client_actions.add("reload", "instance.web.Reload");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Client action to go back in breadcrumb history.
|
* Client action to go back in breadcrumb history.
|
||||||
* If can't go back in history stack, will go back to home.
|
* If can't go back in history stack, will go back to home.
|
||||||
*/
|
*/
|
||||||
instance.web.HistoryBack = instance.web.Widget.extend({
|
instance.web.HistoryBack = function(parent, params) {
|
||||||
init: function(parent, params) {
|
|
||||||
if (!parent.history_back()) {
|
if (!parent.history_back()) {
|
||||||
window.location = '/' + (window.location.search || '');
|
window.location = '/' + (window.location.search || '');
|
||||||
}
|
}
|
||||||
}
|
};
|
||||||
});
|
|
||||||
instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
|
instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -313,6 +313,15 @@ instance.web.ActionManager = instance.web.Widget.extend({
|
||||||
ir_actions_client: function (action, on_close, clear_breadcrumbs) {
|
ir_actions_client: function (action, on_close, clear_breadcrumbs) {
|
||||||
var self = this;
|
var self = this;
|
||||||
var ClientWidget = instance.web.client_actions.get_object(action.tag);
|
var ClientWidget = instance.web.client_actions.get_object(action.tag);
|
||||||
|
|
||||||
|
if (!(ClientWidget.prototype instanceof instance.web.Widget)) {
|
||||||
|
var next;
|
||||||
|
if (next = ClientWidget(this, action.params)) {
|
||||||
|
return this.do_action(next, on_close, clear_breadcrumbs);
|
||||||
|
}
|
||||||
|
return $.when();
|
||||||
|
}
|
||||||
|
|
||||||
return this.ir_actions_common({
|
return this.ir_actions_common({
|
||||||
widget: function () { return new ClientWidget(self, action.params); },
|
widget: function () { return new ClientWidget(self, action.params); },
|
||||||
action: action,
|
action: action,
|
||||||
|
|
|
@ -1,15 +1,18 @@
|
||||||
|
.. highlight:: javascript
|
||||||
|
|
||||||
Creating a new client action
|
Creating a new client action
|
||||||
============================
|
============================
|
||||||
|
|
||||||
Client actions are the client-side of OpenERP's "Server Actions": instead of
|
Client actions are the client-side version of OpenERP's "Server
|
||||||
allowing for semi-arbitrary code to be executed in the server, they allow
|
Actions": instead of allowing for semi-arbitrary code to be executed
|
||||||
for execution of client-customized code.
|
in the server, they allow for execution of client-customized code.
|
||||||
|
|
||||||
On the server side, a client action is an action of type ``ir.actions.client``,
|
On the server side, a client action is an action of type
|
||||||
which has (at most) two properties: a mandatory ``tag``, which is an arbitrary
|
``ir.actions.client``, which has (at most) two properties: a mandatory
|
||||||
string by which the client will identify the action, and an optional ``params``
|
``tag``, which is an arbitrary string by which the client will
|
||||||
which is simply a map of keys and values sent to the client as-is (this way,
|
identify the action, and an optional ``params`` which is simply a map
|
||||||
client actions can be made generic and reused in multiple contexts).
|
of keys and values sent to the client as-is (this way, client actions
|
||||||
|
can be made generic and reused in multiple contexts).
|
||||||
|
|
||||||
General Structure
|
General Structure
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -17,34 +20,48 @@ General Structure
|
||||||
In the OpenERP Web code, a client action only requires two pieces of
|
In the OpenERP Web code, a client action only requires two pieces of
|
||||||
information:
|
information:
|
||||||
|
|
||||||
* Mapping the action's ``tag`` to an OpenERP Web object
|
* Mapping the action's ``tag`` to an object
|
||||||
|
|
||||||
* The OpenERP Web object itself, which must inherit from
|
* Providing said object. Two different types of objects can be mapped
|
||||||
|
to a client action:
|
||||||
|
|
||||||
|
* An OpenERP Web widget, which must inherit from
|
||||||
:js:class:`openerp.web.Widget`
|
:js:class:`openerp.web.Widget`
|
||||||
|
|
||||||
Our example will be the actual code for the widgets client action (a client
|
* A regular javascript function
|
||||||
action displaying a ``res.widget`` object, used in the homepage dashboard of
|
|
||||||
the web client):
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
The major difference is in the lifecycle of these:
|
||||||
|
|
||||||
|
* if the client action maps to a function, the function will simply be
|
||||||
|
called when executing the action. The function can have no further
|
||||||
|
interaction with the Web Client itself, although it can return an
|
||||||
|
action which will be executed after it.
|
||||||
|
|
||||||
|
* if, on the other hand, the client action maps to a
|
||||||
|
:js:class:`~openerp.web.Widget`, that
|
||||||
|
:js:class:`~openerp.web.Widget` will be instantiated and added to
|
||||||
|
the web client's canvas, with the usual
|
||||||
|
:js:class:`~openerp.web.Widget` lifecycle (essentially, it will
|
||||||
|
either take over the content area of the client or it will be
|
||||||
|
integrated within a dialog).
|
||||||
|
|
||||||
|
For example, to create a client action displaying a ``res.widget``
|
||||||
|
object::
|
||||||
|
|
||||||
// Registers the object 'openerp.web_dashboard.Widget' to the client
|
// Registers the object 'openerp.web_dashboard.Widget' to the client
|
||||||
// action tag 'board.home.widgets'
|
// action tag 'board.home.widgets'
|
||||||
openerp.web.client_actions.add(
|
instance.web.client_actions.add(
|
||||||
'board.home.widgets', 'openerp.web_dashboard.Widget');
|
'board.home.widgets', 'openerp.web_dashboard.Widget');
|
||||||
// This object inherits from View, but only Widget is required
|
instance.web_dashboard.Widget = instance.web.Widget.extend({
|
||||||
openerp.web_dashboard.Widget = openerp.web.View.extend({
|
|
||||||
template: 'HomeWidget'
|
template: 'HomeWidget'
|
||||||
});
|
});
|
||||||
|
|
||||||
At this point, the generic ``Widget`` lifecycle takes over, the template is
|
At this point, the generic :js:class:`~openerp.web.Widget` lifecycle
|
||||||
rendered, inserted in the client DOM, bound on the object's ``$element``
|
takes over, the template is rendered, inserted in the client DOM,
|
||||||
property and the object is started.
|
bound on the object's ``$el`` property and the object is started.
|
||||||
|
|
||||||
If the client action takes parameters, these parameters are passed in as a
|
If the client action takes parameters, these parameters are passed in as a
|
||||||
second positional parameter to the constructor:
|
second positional parameter to the constructor::
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
init: function (parent, params) {
|
init: function (parent, params) {
|
||||||
// execute the Widget's init
|
// execute the Widget's init
|
||||||
|
@ -54,15 +71,19 @@ second positional parameter to the constructor:
|
||||||
this.widget_id = params.widget_id;
|
this.widget_id = params.widget_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
More complex initialization (DOM manipulations, RPC requests, ...) should be
|
More complex initialization (DOM manipulations, RPC requests, ...)
|
||||||
performed in the ``start()`` method.
|
should be performed in the :js:func:`~openerp.web.Widget.start()`
|
||||||
|
method.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
As required by ``Widget``'s contract, if ``start`` executes any
|
|
||||||
asynchronous code it should return a ``$.Deferred`` so callers know when
|
|
||||||
it's ready for interaction.
|
|
||||||
|
|
||||||
Although generally speaking client actions are not really interacted with.
|
As required by :js:class:`~openerp.web.Widget`'s contract, if
|
||||||
|
:js:func:`~openerp.web.Widget.start()` executes any asynchronous
|
||||||
|
code it should return a ``$.Deferred`` so callers know when it's
|
||||||
|
ready for interaction.
|
||||||
|
|
||||||
|
Although generally speaking client actions are not really
|
||||||
|
interacted with.
|
||||||
|
|
||||||
.. code-block:: javascript
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
@ -70,22 +91,21 @@ performed in the ``start()`` method.
|
||||||
return $.when(
|
return $.when(
|
||||||
this._super(),
|
this._super(),
|
||||||
// Simply read the res.widget object this action should display
|
// Simply read the res.widget object this action should display
|
||||||
new openerp.web.DataSet(this, 'res.widget').read_ids(
|
new instance.web.Model('res.widget').call(
|
||||||
[this.widget_id], ['title'], this.on_widget_loaded));
|
'read', [[this.widget_id], ['title']])
|
||||||
|
.then(this.proxy('on_widget_loaded'));
|
||||||
}
|
}
|
||||||
|
|
||||||
The client action can then behave exactly as it wishes to within its root
|
The client action can then behave exactly as it wishes to within its
|
||||||
(``this.$element``). In this case, it performs further renderings once its
|
root (``this.$el``). In this case, it performs further renderings once
|
||||||
widget's content is retrieved:
|
its widget's content is retrieved::
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
on_widget_loaded: function (widgets) {
|
on_widget_loaded: function (widgets) {
|
||||||
var widget = widgets[0];
|
var widget = widgets[0];
|
||||||
var url = _.sprintf(
|
var url = _.sprintf(
|
||||||
'/web_dashboard/widgets/content?session_id=%s&widget_id=%d',
|
'/web_dashboard/widgets/content?session_id=%s&widget_id=%d',
|
||||||
this.session.session_id, widget.id);
|
this.session.session_id, widget.id);
|
||||||
this.$element.html(QWeb.render('HomeWidget.content', {
|
this.$el.html(QWeb.render('HomeWidget.content', {
|
||||||
widget: widget,
|
widget: widget,
|
||||||
url: url
|
url: url
|
||||||
}));
|
}));
|
||||||
|
|
|
@ -1,78 +0,0 @@
|
||||||
Adding a sidebar to a view
|
|
||||||
==========================
|
|
||||||
|
|
||||||
Initialization
|
|
||||||
--------------
|
|
||||||
|
|
||||||
Each view has the responsibility to create its sidebar (or not) if and only if
|
|
||||||
the ``sidebar`` flag is set in its options.
|
|
||||||
|
|
||||||
In that case, it should use the ``sidebar_id`` value (from its options) to
|
|
||||||
initialize the sidebar at the right position in the DOM:
|
|
||||||
|
|
||||||
.. code-block:: javascript
|
|
||||||
|
|
||||||
if (this.options.sidebar && this.options.sidebar_id) {
|
|
||||||
this.sidebar = new openerp.web.Sidebar(this, this.options.sidebar_id);
|
|
||||||
this.sidebar.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
Because the sidebar is an old-style widget, it must be started after being
|
|
||||||
initialized.
|
|
||||||
|
|
||||||
Sidebar communication protocol
|
|
||||||
------------------------------
|
|
||||||
|
|
||||||
In order to behave correctly, a sidebar needs informations from its parent
|
|
||||||
view.
|
|
||||||
|
|
||||||
This information is extracted via a very basic protocol consisting of a
|
|
||||||
property and two methods:
|
|
||||||
|
|
||||||
.. js:attribute:: dataset
|
|
||||||
|
|
||||||
the view's dataset, used to fetch the currently active model and provide it
|
|
||||||
to remote action handlers as part of the basic context
|
|
||||||
|
|
||||||
.. js:function:: get_selected_ids()
|
|
||||||
|
|
||||||
Used to query the parent view for the set of currently available record
|
|
||||||
identifiers. Used to setup the basic context's ``active_id`` and
|
|
||||||
``active_ids`` keys.
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
|
|
||||||
:js:func:`get_selected_ids` must return at least one id
|
|
||||||
|
|
||||||
:returns: an array of at least one id
|
|
||||||
:rtype: Array<Number>
|
|
||||||
|
|
||||||
.. js:function:: sidebar_context()
|
|
||||||
|
|
||||||
Queries the view for additional context data to provide to the sidebar.
|
|
||||||
|
|
||||||
:js:class:`~openerp.base.View` provides a default NOOP implementation,
|
|
||||||
which simply resolves to an empty object.
|
|
||||||
|
|
||||||
:returns: a promise yielding an object on success, this object is mergeed
|
|
||||||
into the sidebar's own context
|
|
||||||
:rtype: $.Deferred<Object>
|
|
||||||
|
|
||||||
Programmatic folding and unfolding
|
|
||||||
----------------------------------
|
|
||||||
|
|
||||||
The sidebar object starts folded. It provides three methods to handle its
|
|
||||||
folding status:
|
|
||||||
|
|
||||||
.. js:function:: do_toggle
|
|
||||||
|
|
||||||
Toggles the status of the sidebar
|
|
||||||
|
|
||||||
.. js:function:: do_fold
|
|
||||||
|
|
||||||
Forces the sidebar closed if it's currently open
|
|
||||||
|
|
||||||
.. js:function:: do_unfold
|
|
||||||
|
|
||||||
Forces the sidebar open if it's currently closed
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ Contents:
|
||||||
|
|
||||||
templates
|
templates
|
||||||
|
|
||||||
|
guides/client-action
|
||||||
|
|
||||||
Indices and tables
|
Indices and tables
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue