summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic/basic_controller.js
diff options
context:
space:
mode:
authorAaron Bohy <aab@odoo.com>2017-04-12 10:44:06 +0200
committerAaron Bohy <aab@odoo.com>2017-04-12 10:44:06 +0200
commit3638cade098fe8531b1f34a0481aadd090f1ea09 (patch)
tree35e9bc3bcf8be0b52b6ddb929fa7a9d2894c5da4 /addons/web/static/src/js/views/basic/basic_controller.js
parentf1168d06c0415092c4adc9c2c6917ac5e67a1420 (diff)
parente2e0353ce576f3c38814ae39ca4ab18ff0b6785f (diff)
[MERGE] web,*: redesign all JS views
This branch introduces a full redesign of all JS views. We started basically from scratch. The goal was to unify all the various views under a common framework, to make them testable, to make then usable in different conditions (in studio, or in the frontend), and to make our lives easier. Some important points are: - we have a brand new testing framework (still QUnit based) - kanban view (formerly in web_kanban) moved to the web addon - calendar view (formerly in web_calendar) moved to web as well - tree view was removed - each view is now separated into 3 parts: the renderer, the model, the controller (so, actually a kind of MVC) - we introduced new coding guidelines (camelCase, 80 chars width, ...) - all new code should be documented We hope that this code is the start of a new era for the Odoo web client, we want to have a high quality codebase, well documented, well tested, well designed. Work done by the framework team: mostly aab, ged, chm, dmo, qsm, mge, fwi, adr
Diffstat (limited to 'addons/web/static/src/js/views/basic/basic_controller.js')
-rw-r--r--addons/web/static/src/js/views/basic/basic_controller.js202
1 files changed, 202 insertions, 0 deletions
diff --git a/addons/web/static/src/js/views/basic/basic_controller.js b/addons/web/static/src/js/views/basic/basic_controller.js
new file mode 100644
index 00000000000..c21d7a43335
--- /dev/null
+++ b/addons/web/static/src/js/views/basic/basic_controller.js
@@ -0,0 +1,202 @@
+odoo.define('web.BasicController', function (require) {
+"use strict";
+
+/**
+ * The BasicController is mostly here to share code between views that will use
+ * a BasicModel (or a subclass). Currently, the BasicViews are the form, list
+ * and kanban views.
+ */
+
+var AbstractController = require('web.AbstractController');
+var FieldManagerMixin = require('web.FieldManagerMixin');
+var core = require('web.core');
+var Dialog = require('web.Dialog');
+var Pager = require('web.Pager');
+
+var _t = core._t;
+
+return AbstractController.extend(FieldManagerMixin, {
+ custom_events: _.extend({}, AbstractController.prototype.custom_events, FieldManagerMixin.custom_events, {
+ reload: '_onReload',
+ }),
+ /**
+ * @override
+ * @param {Object} params
+ * @param {boolean} params.archiveEnabled
+ * @param {boolean} params.confirmOnDelete
+ * @param {boolean} params.hasButtons
+ */
+ init: function (parent, model, renderer, params) {
+ this._super.apply(this, arguments);
+ this.archiveEnabled = params.archiveEnabled;
+ this.confirmOnDelete = params.confirmOnDelete;
+ this.hasButtons = params.hasButtons;
+ FieldManagerMixin.init.call(this, this.model);
+ this.handle = params.initialState.id;
+ this.isDirty = false;
+ },
+ /**
+ * @override
+ * @returns {Deferred}
+ */
+ start: function () {
+ return this._super.apply(this, arguments)
+ .then(this._updateEnv.bind(this));
+ },
+
+ //--------------------------------------------------------------------------
+ // Public
+ //--------------------------------------------------------------------------
+
+ /**
+ * @override
+ */
+ renderPager: function ($node, options) {
+ var data = this.model.get(this.handle, {raw: true});
+ this.pager = new Pager(this, data.count, data.offset + 1, data.limit, options);
+
+ this.pager.on('pager_changed', this, function (newState) {
+ var self = this;
+ this.pager.disable();
+ var limitChanged = (data.limit !== newState.limit);
+ this.reload({limit: newState.limit, offset: newState.current_min - 1})
+ .then(function () {
+ // Reset the scroll position to the top on page changed only
+ if (!limitChanged) {
+ self.trigger_up('scrollTo', {offset: 0});
+ }
+ })
+ .then(this.pager.enable.bind(this.pager));
+ });
+ this.pager.appendTo($node);
+ this._updatePager(); // to force proper visibility
+ },
+ /**
+ * @override
+ * @returns {Deferred}
+ */
+ update: function (params, options) {
+ var self = this;
+ return this._super(params, options).then(function () {
+ self._updateEnv();
+ self._updatePager();
+ });
+ },
+
+ //--------------------------------------------------------------------------
+ // Private
+ //--------------------------------------------------------------------------
+
+ /**
+ * Delete records (and ask for confirmation if necessary)
+ *
+ * @param {string[]} ids list of local record ids
+ */
+ _deleteRecords: function (ids) {
+ var self = this;
+ function doIt() {
+ return self.model
+ .deleteRecords(ids, self.modelName, self.handle)
+ .then(self._onDeletedRecords.bind(self, ids));
+ }
+ if (this.confirmOnDelete) {
+ Dialog.confirm(this, _t("Are you sure you want to delete this record ?"), {
+ confirm_callback: doIt,
+ });
+ } else {
+ doIt();
+ }
+ },
+ /**
+ * Used by list and kanban views to determine whether or not to display
+ * the no content helper (if there is no data in the state to display)
+ *
+ * @private
+ * @param {Object} state
+ * @returns {boolean}
+ */
+ _hasContent: function (state) {
+ return state.count !== 0;
+ },
+ /**
+ * Hook method, called when record(s) has been deleted.
+ *
+ * @see _deleteRecord
+ * @param {string[]} ids list of deleted ids (basic model local handles)
+ */
+ _onDeletedRecords: function (ids) {
+ this.update({});
+ },
+ /**
+ * Helper method, to get the current environment variables from the model
+ * and notifies the component chain (by bubbling an event up)
+ */
+ _updateEnv: function () {
+ var env = this.model.get(this.handle, {env: true});
+ this.trigger_up('env_updated', env);
+ },
+ /**
+ * Helper method, to make sure the information displayed by the pager is up
+ * to date.
+ */
+ _updatePager: function () {
+ if (this.pager) {
+ var data = this.model.get(this.handle, {raw: true});
+ this.pager.updateState({
+ current_min: data.offset + 1,
+ size: data.count,
+ });
+ var isRecord = data.type === 'record';
+ var hasData = !!data.count;
+ var isGrouped = data.groupedBy ? !!data.groupedBy.length : false;
+ var isNew = this.model.isNew(this.handle);
+ var isPagerVisible = isRecord ? !isNew : (hasData && !isGrouped);
+
+ this.pager.do_toggle(isPagerVisible);
+ }
+ },
+
+ //--------------------------------------------------------------------------
+ // Handlers
+ //--------------------------------------------------------------------------
+
+ /**
+ * We need to keep manually track of the isDirty property. Note that it is
+ * not the same as the one from the basic model. A new record from the
+ * basic model is considered dirty, but from the controller point of view,
+ * there was no user interaction and should not be dirty.
+ *
+ * @private
+ */
+ _onFieldChanged: function () {
+ this.isDirty = true;
+ FieldManagerMixin._onFieldChanged.apply(this, arguments);
+ },
+ /**
+ * When a reload event triggers up, we need to reload the full view.
+ * For example, after a form view dialog saved some data.
+ *
+ * @todo: rename db_id into handle
+ *
+ * @param {OdooEvent} event
+ * @param {Object} event.data
+ * @param {string} [event.data.db_id] handle of the data to reload and
+ * re-render (reload the whole form by default)
+ * @param {string[]} [event.data.fieldNames] list of the record's fields to
+ * reload
+ */
+ _onReload: function (event) {
+ var data = event && event.data || {};
+ var handle = data.db_id;
+ if (handle) {
+ // reload the relational field given its db_id
+ this.model.reload(handle).then(this._confirmSave.bind(this, handle));
+ } else {
+ // no db_id given, so reload the main record
+ this.reload({fieldNames: data.fieldNames});
+ }
+ },
+
+});
+
+});