[IMP] client actions: allow functions, not just widgets

bzr revid: xmo@openerp.com-20121003114258-vp3mg1yqps4qfkyp
This commit is contained in:
Xavier Morel 2012-10-03 13:42:58 +02:00
parent 0b0bce6b01
commit 10513dc524
5 changed files with 85 additions and 139 deletions

View File

@ -614,39 +614,32 @@ instance.web.client_actions.add("login", "instance.web.Login");
* Client action to reload the whole interface.
* If params has an entry 'menu_id', it opens the given menu entry.
*/
instance.web.Reload = instance.web.Widget.extend({
init: function(parent, params) {
this._super(parent);
this.menu_id = (params && params.menu_id) || false;
},
start: function() {
var l = window.location;
instance.web.Reload = function(parent, params) {
var menu_id = (params && params.menu_id) || false;
var l = window.location;
var sobj = $.deparam(l.search.substr(1));
sobj.ts = new Date().getTime();
var search = '?' + $.param(sobj);
var sobj = $.deparam(l.search.substr(1));
sobj.ts = new Date().getTime();
var search = '?' + $.param(sobj);
var hash = l.hash;
if (this.menu_id) {
hash = "#menu_id=" + this.menu_id;
}
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
window.location = url;
var hash = l.hash;
if (menu_id) {
hash = "#menu_id=" + menu_id;
}
});
var url = l.protocol + "//" + l.host + l.pathname + search + hash;
window.location = url;
};
instance.web.client_actions.add("reload", "instance.web.Reload");
/**
* Client action to go back in breadcrumb history.
* If can't go back in history stack, will go back to home.
*/
instance.web.HistoryBack = instance.web.Widget.extend({
init: function(parent, params) {
if (!parent.history_back()) {
window.location = '/' + (window.location.search || '');
}
instance.web.HistoryBack = function(parent, params) {
if (!parent.history_back()) {
window.location = '/' + (window.location.search || '');
}
});
};
instance.web.client_actions.add("history_back", "instance.web.HistoryBack");
/**

View File

@ -313,6 +313,15 @@ instance.web.ActionManager = instance.web.Widget.extend({
ir_actions_client: function (action, on_close, clear_breadcrumbs) {
var self = this;
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({
widget: function () { return new ClientWidget(self, action.params); },
action: action,

View File

@ -1,15 +1,18 @@
.. highlight:: javascript
Creating a new client action
============================
Client actions are the client-side of OpenERP's "Server Actions": instead of
allowing for semi-arbitrary code to be executed in the server, they allow
for execution of client-customized code.
Client actions are the client-side version of OpenERP's "Server
Actions": instead of allowing for semi-arbitrary code to be executed
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``,
which has (at most) two properties: a mandatory ``tag``, which is an arbitrary
string by which the client will identify the action, and an optional ``params``
which is simply a map of keys and values sent to the client as-is (this way,
client actions can be made generic and reused in multiple contexts).
On the server side, a client action is an action of type
``ir.actions.client``, which has (at most) two properties: a mandatory
``tag``, which is an arbitrary string by which the client will
identify the action, and an optional ``params`` which is simply a map
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
-----------------
@ -17,34 +20,48 @@ General Structure
In the OpenERP Web code, a client action only requires two pieces of
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
:js:class:`openerp.web.Widget`
* Providing said object. Two different types of objects can be mapped
to a client action:
Our example will be the actual code for the widgets client action (a client
action displaying a ``res.widget`` object, used in the homepage dashboard of
the web client):
* An OpenERP Web widget, which must inherit from
:js:class:`openerp.web.Widget`
.. code-block:: javascript
* A regular javascript function
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
// action tag 'board.home.widgets'
openerp.web.client_actions.add(
instance.web.client_actions.add(
'board.home.widgets', 'openerp.web_dashboard.Widget');
// This object inherits from View, but only Widget is required
openerp.web_dashboard.Widget = openerp.web.View.extend({
instance.web_dashboard.Widget = instance.web.Widget.extend({
template: 'HomeWidget'
});
At this point, the generic ``Widget`` lifecycle takes over, the template is
rendered, inserted in the client DOM, bound on the object's ``$element``
property and the object is started.
At this point, the generic :js:class:`~openerp.web.Widget` lifecycle
takes over, the template is rendered, inserted in the client DOM,
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
second positional parameter to the constructor:
.. code-block:: javascript
second positional parameter to the constructor::
init: function (parent, params) {
// execute the Widget's init
@ -54,15 +71,19 @@ second positional parameter to the constructor:
this.widget_id = params.widget_id;
}
More complex initialization (DOM manipulations, RPC requests, ...) should be
performed in the ``start()`` method.
More complex initialization (DOM manipulations, RPC requests, ...)
should be performed in the :js:func:`~openerp.web.Widget.start()`
method.
.. 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
@ -70,22 +91,21 @@ performed in the ``start()`` method.
return $.when(
this._super(),
// Simply read the res.widget object this action should display
new openerp.web.DataSet(this, 'res.widget').read_ids(
[this.widget_id], ['title'], this.on_widget_loaded));
new instance.web.Model('res.widget').call(
'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
(``this.$element``). In this case, it performs further renderings once its
widget's content is retrieved:
.. code-block:: javascript
The client action can then behave exactly as it wishes to within its
root (``this.$el``). In this case, it performs further renderings once
its widget's content is retrieved::
on_widget_loaded: function (widgets) {
var widget = widgets[0];
var url = _.sprintf(
'/web_dashboard/widgets/content?session_id=%s&widget_id=%d',
this.session.session_id, widget.id);
this.$element.html(QWeb.render('HomeWidget.content', {
this.$el.html(QWeb.render('HomeWidget.content', {
widget: widget,
url: url
}));

View File

@ -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

View File

@ -24,6 +24,8 @@ Contents:
templates
guides/client-action
Indices and tables
==================