summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/list
diff options
context:
space:
mode:
authorJérémy Hennecart <jeh@odoo.com>2020-06-19 07:06:31 +0000
committerAaron Bohy <aab@odoo.com>2020-06-22 13:04:01 +0000
commitc7964657ecbbad278cec73cf231d7eef55b44347 (patch)
treebe77532a80cbed1283c2062cb2dc1159aa0fbdd4 /addons/web/static/src/js/views/list
parent96fcefb6f1fe3fb5a5f197e2635e6d71d9a1d9dd (diff)
[IMP] web: add support of "sample" attribute in views
With this commit, it is now possible to display sample (fake) data in empty views, in the hope of easing user onboarding. This can be enabled (in list and kanban views) with attribute sample="1" on the arch root element. In this case, if there is no data to display, sample data will be generated based on heuristics (depending on field types and names). This can be used in addition to the no content helper. Task 2232801 X-original-commit: 7c8e627ff5029cb539d702705e5ce53d658c76ed Co-authored-by: Aaron Bohy <aab@odoo.com> Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com> Co-authored-by: Jérémy Hennecart <jeh@odoo.com> Co-authored-by: Julien Mougenot <jum@odoo.com>
Diffstat (limited to 'addons/web/static/src/js/views/list')
-rw-r--r--addons/web/static/src/js/views/list/list_controller.js30
-rw-r--r--addons/web/static/src/js/views/list/list_model.js2
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js119
3 files changed, 78 insertions, 73 deletions
diff --git a/addons/web/static/src/js/views/list/list_controller.js b/addons/web/static/src/js/views/list/list_controller.js
index 709deced4df..51f95e5a9ff 100644
--- a/addons/web/static/src/js/views/list/list_controller.js
+++ b/addons/web/static/src/js/views/list/list_controller.js
@@ -213,20 +213,22 @@ var ListController = BasicController.extend({
_addRecord: function (dataPointId) {
var self = this;
this._disableButtons();
- return this.renderer.unselectRow().then(function () {
- return self.model.addDefaultRecord(dataPointId, {
- position: self.editable,
- });
- }).then(function (recordID) {
- var state = self.model.get(self.handle);
- self.renderer.updateState(state, {keepWidths: true})
- .then(function () {
- self.renderer.editRecord(recordID);
- })
- .then(() => {
- self._updatePaging(state);
+ return this._removeSampleData(() => {
+ return this.renderer.unselectRow().then(function () {
+ return self.model.addDefaultRecord(dataPointId, {
+ position: self.editable,
});
- }).then(this._enableButtons.bind(this)).guardedCatch(this._enableButtons.bind(this));
+ }).then(function (recordID) {
+ var state = self.model.get(self.handle);
+ self._updateRendererState(state, { keepWidths: true })
+ .then(function () {
+ self.renderer.editRecord(recordID);
+ })
+ .then(() => {
+ self._updatePaging(state);
+ });
+ }).then(this._enableButtons.bind(this)).guardedCatch(this._enableButtons.bind(this));
+ });
},
/**
* Assign on the buttons create additionnal behavior to facilitate the work of the users doing input only using the keyboard
@@ -524,7 +526,7 @@ var ListController = BasicController.extend({
*/
_shouldBounceOnClick() {
const state = this.model.get(this.handle, {raw: true});
- return !state.count;
+ return !state.count || state.isSample;
},
/**
* Called when clicking on 'Archive' or 'Unarchive' in the sidebar.
diff --git a/addons/web/static/src/js/views/list/list_model.js b/addons/web/static/src/js/views/list/list_model.js
index 9ba0b473a5a..5d40343ef8d 100644
--- a/addons/web/static/src/js/views/list/list_model.js
+++ b/addons/web/static/src/js/views/list/list_model.js
@@ -25,7 +25,7 @@ odoo.define('web.ListModel', function (require) {
* @override
* @see _readGroupExtraFields
*/
- get: function () {
+ __get: function () {
var result = this._super.apply(this, arguments);
var dp = result && this.localData[result.id];
if (dp && dp.groupData) {
diff --git a/addons/web/static/src/js/views/list/list_renderer.js b/addons/web/static/src/js/views/list/list_renderer.js
index c2c57250022..428a781319f 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -35,6 +35,7 @@ var FIELD_CLASSES = {
};
var ListRenderer = BasicRenderer.extend({
+ className: 'o_list_view',
events: {
"mousedown": "_onMouseDown",
"click .o_optional_columns_dropdown .dropdown-item": "_onToggleOptionalColumn",
@@ -1021,80 +1022,82 @@ var ListRenderer = BasicRenderer.extend({
* this method does not wait for the field widgets to be ready.
*
* @override
- * @private
* @returns {Promise} resolved when the view has been rendered
*/
- _renderView: function () {
- var self = this;
-
+ async _renderView() {
const oldPagers = this.pagers;
- this.pagers = [];
+ let prom;
+ let tableWrapper;
+ if (this.state.count > 0 || !this.noContentHelp) {
+ // render a table if there are records, or if there is no no content
+ // helper (empty table in this case)
+ this.pagers = [];
- // display the no content helper if there is no data to display
- var displayNoContentHelper = !this._hasContent() && !!this.noContentHelp;
- this.$el.toggleClass('o_list_view', !displayNoContentHelper);
- if (displayNoContentHelper) {
- // destroy the previously instantiated pagers, if any
- oldPagers.forEach(pager => pager.destroy());
+ const orderedBy = this.state.orderedBy;
+ this.hasHandle = orderedBy.length === 0 || orderedBy[0].name === this.handleField;
+ this._computeAggregates();
- this.$el.removeClass('table-responsive');
- this.$el.html(this._renderNoContentHelper());
- return this._super.apply(this, arguments);
+ const $table = $(
+ '<table class="o_list_table table table-sm table-hover table-striped"/>'
+ );
+ $table.toggleClass('o_list_table_grouped', this.isGrouped);
+ $table.toggleClass('o_list_table_ungrouped', !this.isGrouped);
+ const defs = [];
+ this.defs = defs;
+ if (this.isGrouped) {
+ $table.append(this._renderHeader());
+ $table.append(this._renderGroups(this.state.data));
+ $table.append(this._renderFooter());
+
+ } else {
+ $table.append(this._renderHeader());
+ $table.append(this._renderBody());
+ $table.append(this._renderFooter());
+ }
+ tableWrapper = Object.assign(document.createElement('div'), {
+ className: 'table-responsive',
+ });
+ tableWrapper.appendChild($table[0]);
+ delete this.defs;
+ prom = Promise.all(defs);
}
- var orderedBy = this.state.orderedBy;
- this.hasHandle = orderedBy.length === 0 || orderedBy[0].name === this.handleField;
- this._computeAggregates();
+ await Promise.all([this._super.apply(this, arguments), prom]);
- var $table = $('<table>').addClass('o_list_table table table-sm table-hover table-striped');
- $table.toggleClass('o_list_table_grouped', this.isGrouped);
- $table.toggleClass('o_list_table_ungrouped', !this.isGrouped);
- var defs = [];
- this.defs = defs;
- if (this.isGrouped) {
- $table.append(this._renderHeader());
- $table.append(this._renderGroups(this.state.data));
- $table.append(this._renderFooter());
+ this.el.innerHTML = "";
+ this.el.classList.remove('o_list_optional_columns');
- } else {
- $table.append(this._renderHeader());
- $table.append(this._renderBody());
- $table.append(this._renderFooter());
- }
- const tableWrapper = Object.assign(document.createElement('div'), {
- className: 'table-responsive',
- });
- tableWrapper.appendChild($table[0]);
- delete this.defs;
-
- var prom = Promise.all(defs).then(() => {
- // destroy the previously instantiated pagers, if any
- oldPagers.forEach(pager => pager.destroy());
+ // destroy the previously instantiated pagers, if any
+ oldPagers.forEach(pager => pager.destroy());
- // Append the table to the main element
- self.el.innerHTML = "";
- dom.append(self.el, tableWrapper, {
+ // append the table (if any) to the main element
+ if (tableWrapper) {
+ dom.append(this.el, tableWrapper, {
callbacks: [{ widget: this }],
- in_DOM: document.body.contains(self.el),
+ in_DOM: document.body.contains(this.el),
});
-
- if (self.optionalColumns.length) {
- self.$el.addClass('o_list_optional_columns');
- self.$('table').append($('<i class="o_optional_columns_dropdown_toggle fa fa-ellipsis-v"/>'));
- self.$el.append(self._renderOptionalColumnsDropdown());
+ if (this.optionalColumns.length) {
+ this.el.classList.add('o_list_optional_columns');
+ this.$('table').append(
+ $('<i class="o_optional_columns_dropdown_toggle fa fa-ellipsis-v"/>')
+ );
+ this.$el.append(this._renderOptionalColumnsDropdown());
}
-
- if (self.selection.length) {
- var $checked_rows = self.$('tr').filter(function (index, el) {
- return _.contains(self.selection, $(el).data('id'));
- });
+ if (this.selection.length) {
+ const $checked_rows = this.$('tr').filter(
+ (i, el) => this.selection.includes(el.dataset.id)
+ );
$checked_rows.find('.o_list_record_selector input').prop('checked', true);
- if ($checked_rows.length === self.$('.o_data_row').length) { // all rows are checked
- self.$('thead .o_list_record_selector input').prop('checked', true);
+ if ($checked_rows.length === this.$('.o_data_row').length) { // all rows are checked
+ this.$('thead .o_list_record_selector input').prop('checked', true);
}
}
- });
- return Promise.all([this._super.apply(this, arguments), prom]);
+ }
+
+ // display the no content helper if necessary
+ if (!this._hasContent() && !!this.noContentHelp) {
+ this.$el.append(this._renderNoContentHelper());
+ }
},
/**
* Each line or cell can be decorated according to a few simple rules. The