summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/abstract_controller.js
diff options
context:
space:
mode:
Diffstat (limited to 'addons/web/static/src/js/views/abstract_controller.js')
-rw-r--r--addons/web/static/src/js/views/abstract_controller.js170
1 files changed, 137 insertions, 33 deletions
diff --git a/addons/web/static/src/js/views/abstract_controller.js b/addons/web/static/src/js/views/abstract_controller.js
index 3bdf8ab08ca..150b4803a7a 100644
--- a/addons/web/static/src/js/views/abstract_controller.js
+++ b/addons/web/static/src/js/views/abstract_controller.js
@@ -12,12 +12,16 @@ odoo.define('web.AbstractController', function (require) {
* reading localstorage, ...) has to go through the controller.
*/
+var ControlPanelMixin = require('web.ControlPanelMixin');
+var core = require('web.core');
var Widget = require('web.Widget');
+var QWeb = core.qweb;
-var AbstractController = Widget.extend({
+var AbstractController = Widget.extend(ControlPanelMixin, {
custom_events: {
open_record: '_onOpenRecord',
+ switch_view: '_onSwitchView',
},
events: {
'click a[type="action"]': '_onActionClicked',
@@ -30,8 +34,15 @@ var AbstractController = Widget.extend({
* @param {AbstractRenderer} renderer
* @param {object} params
* @param {string} params.modelName
+ * @param {string} [params.controllerID] an id to ease the communication
+ * with upstream components
* @param {any} [params.handle] a handle that will be given to the model (some id)
* @param {any} params.initialState the initialState
+ * @param {boolean} params.isMultiRecord
+ * @param {Object[]} params.actionViews
+ * @param {string} params.viewType
+ * @param {boolean} params.withControlPanel set to false to hide the
+ * ControlPanel
*/
init: function (parent, model, renderer, params) {
this._super.apply(this, arguments);
@@ -40,7 +51,23 @@ var AbstractController = Widget.extend({
this.modelName = params.modelName;
this.handle = params.handle;
this.activeActions = params.activeActions;
+ this.controllerID = params.controllerID;
this.initialState = params.initialState;
+
+ // those arguments are temporary, they won't be necessary as soon as the
+ // ControlPanel will be handled by the View
+ this.displayName = params.displayName;
+ this.isMultiRecord = params.isMultiRecord;
+ this.searchable = params.searchable;
+ this.searchView = params.searchView;
+ this.searchViewHidden = params.searchViewHidden;
+ this.actionViews = params.actionViews;
+ this.viewType = params.viewType;
+ this.withControlPanel = params.withControlPanel !== false;
+ // override this.need_control_panel so that the ActionManager doesn't
+ // update the control panel when it isn't visible (this is a temporary
+ // hack that can be removed as soon as the CP'll be handled by the view)
+ this.need_control_panel = this.withControlPanel;
},
/**
* Simply renders and updates the url.
@@ -48,10 +75,19 @@ var AbstractController = Widget.extend({
* @returns {Deferred}
*/
start: function () {
+ var self = this;
+
+ this.$el.addClass('o_view_controller');
+
+ // render the ControlPanel elements (buttons, pager, sidebar...)
+ this.controlPanelElements = this._renderControlPanelElements();
+
return $.when(
this._super.apply(this, arguments),
this.renderer.appendTo(this.$el)
- ).then(this._update.bind(this, this.initialState));
+ ).then(function () {
+ self._update(self.initialState);
+ });
},
/**
* @override
@@ -60,6 +96,9 @@ var AbstractController = Widget.extend({
if (this.$buttons) {
this.$buttons.off();
}
+ if (this.controlPanelElements && this.controlPanelElements.$switch_buttons) {
+ this.controlPanelElements.$switch_buttons.off();
+ }
return this._super.apply(this, arguments);
},
@@ -70,7 +109,7 @@ var AbstractController = Widget.extend({
/**
* Discards the changes made on the record associated to the given ID, or
* all changes made by the current controller if no recordID is given. For
- * example, when the user open the 'home' screen, the view manager will call
+ * example, when the user opens the 'home' screen, the action manager calls
* this method on the active view to make sure it is ok to open the home
* screen (and lose all current state).
*
@@ -96,23 +135,15 @@ var AbstractController = Widget.extend({
return {};
},
/**
- * @see setScrollTop
- * @returns {number}
- */
- getScrollTop: function () {
- return this.scrollTop;
- },
- /**
* Returns a title that may be displayed in the breadcrumb area. For
* example, the name of the record.
*
- * Note: this seems wrong right now, it should not be implemented, we have
- * no guarantee that there is a display_name variable in a controller.
+ * note: this will be moved to AbstractAction
*
* @returns {string}
*/
getTitle: function () {
- return this.display_name;
+ return this.displayName;
},
/**
* The use of this method is discouraged. It is still snakecased, because
@@ -136,12 +167,9 @@ var AbstractController = Widget.extend({
return this.update(params || {});
},
/**
- * Most likely called by the view manager, this method is responsible for
- * adding buttons in the control panel (buttons such as save/discard/...)
- *
- * Note that there is no guarantee that this method will be called. The
- * controller is supposed to work even without a view manager, for example
- * in the frontend (odoo frontend = public website)
+ * Renders the buttons to append, in most cases, to the control panel (in
+ * the bottom left corner). When the view is rendered in a dialog, those
+ * buttons might be moved to the dialog's footer.
*
* @param {jQuery Node} $node
*/
@@ -153,7 +181,7 @@ var AbstractController = Widget.extend({
* controller can actually render whatever he wants in the pager zone. If
* your view does not want a pager, just let this method empty.
*
- * @param {Query Node} $node
+ * @param {jQuery Node} $node
*/
renderPager: function ($node) {
},
@@ -161,21 +189,11 @@ var AbstractController = Widget.extend({
* Same as renderPager, but for the 'sidebar' zone (the zone with the menu
* dropdown in the control panel next to the buttons)
*
- * @param {Query Node} $node
+ * @param {jQuery Node} $node
*/
renderSidebar: function ($node) {
},
/**
- * Not sure about this one, it probably needs to be reworked, maybe merged
- * in get/set local state methods.
- *
- * @see getScrollTop
- * @param {number} scrollTop
- */
- setScrollTop: function (scrollTop) {
- this.scrollTop = scrollTop;
- },
- /**
* This is the main entry point for the controller. Changes from the search
* view arrive in this method, and internal changes can sometimes also call
* this method. It is basically the way everything notifies the controller
@@ -219,7 +237,68 @@ var AbstractController = Widget.extend({
* world
*/
_pushState: function (state) {
- this.trigger_up('push_state', state || {});
+ this.trigger_up('push_state', {
+ controllerID: this.controllerID,
+ state: state || {},
+ });
+ },
+ /**
+ * Renders the control elements (buttons, pager and sidebar) of the current
+ * view.
+ *
+ * @private
+ * @returns {Object} an object containing the control panel jQuery elements
+ */
+ _renderControlPanelElements: function () {
+ var elements = {};
+
+ if (this.withControlPanel) {
+ elements = {
+ $buttons: $('<div>'),
+ $sidebar: $('<div>'),
+ $pager: $('<div>'),
+ };
+
+ this.renderButtons(elements.$buttons);
+ this.renderSidebar(elements.$sidebar);
+ this.renderPager(elements.$pager);
+ // remove the unnecessary outer div
+ elements = _.mapObject(elements, function($node) {
+ return $node && $node.contents();
+ });
+ elements.$switch_buttons = this._renderSwitchButtons();
+ }
+
+ return elements;
+ },
+ /**
+ * Renders the switch buttons and binds listeners on them.
+ *
+ * @private
+ * @returns {jQuery}
+ */
+ _renderSwitchButtons: function () {
+ var self = this;
+ var views = _.filter(this.actionViews, {multiRecord: this.isMultiRecord});
+
+ if (views.length <= 1) {
+ return $();
+ }
+
+ var $switchButtons = $(QWeb.render('ControlPanel.SwitchButtons', {
+ views: views,
+ }));
+ // create bootstrap tooltips
+ _.each(views, function (view) {
+ $switchButtons.filter('.o_cp_switch_' + view.type).tooltip();
+ });
+ // add onclick event listener
+ $switchButtons.filter('button').click(_.debounce(function (event) {
+ var viewType = $(event.target).data('view-type');
+ self.trigger_up('switch_view', {view_type: viewType});
+ }, 200, true));
+
+ return $switchButtons;
},
/**
* This method is called after each update or when the start method is
@@ -235,6 +314,22 @@ var AbstractController = Widget.extend({
* @returns {Deferred}
*/
_update: function (state) {
+ // AAB: update the control panel -> this will be moved elsewhere at some point
+ var cpContent = _.extend({}, this.controlPanelElements);
+ if (this.searchView) {
+ _.extend(cpContent, {
+ $searchview: this.searchView.$el,
+ $searchview_buttons: this.searchView.$buttons.contents(),
+ });
+ }
+ this.update_control_panel({
+ active_view_selector: '.o_cp_switch_' + this.viewType,
+ cp_content: cpContent,
+ hidden: !this.withControlPanel,
+ searchview: this.searchView,
+ search_view_hidden: !this.searchable || this.searchviewHidden,
+ });
+
this._pushState();
return $.when();
},
@@ -249,7 +344,7 @@ var AbstractController = Widget.extend({
*
* Note: this method seems wrong, it relies on the model being a basic model,
* to get the res_id. It should receive the res_id in the event data
- * @todo move this to basic controller? or view manager
+ * @todo move this to basic controller?
*
* @private
* @param {OdooEvent} event
@@ -278,6 +373,15 @@ var AbstractController = Widget.extend({
event.preventDefault();
this.do_action(event.target.name);
},
+ /**
+ * Intercepts the 'switch_view' event to add the controllerID into the data,
+ * and lets the event bubble up.
+ *
+ * @param {OdooEvent} event
+ */
+ _onSwitchView: function (event) {
+ event.data.controllerID = this.controllerID;
+ },
});