[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,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");
|
||||
|
||||
/**
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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
|
||||
}));
|
||||
|
|
|
@ -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
|
||||
|
||||
guides/client-action
|
||||
|
||||
Indices and tables
|
||||
==================
|
||||
|
||||
|
|
Loading…
Reference in New Issue