summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views
diff options
context:
space:
mode:
authorMartin Trigaux <mat@odoo.com>2019-07-15 07:07:48 +0000
committerMartin Trigaux <mat@odoo.com>2019-07-15 08:12:26 +0000
commitfd15ca19183a765e1307c4d197bf0dda30b73ab7 (patch)
tree896042073ebc752e8bcf535f21696320c69bbe13 /addons/web/static/src/js/views
parent6b8a0beda65dce688ecd0ee34828097b67a13dab (diff)
parent1f5a4649a67908ecb6acfec37673c8149de0471f (diff)
[MERGE] Forward port of saas-12.4 to master up to 1f5a4649a67908
closes odoo/odoo#34850 Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
Diffstat (limited to 'addons/web/static/src/js/views')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js93
-rw-r--r--addons/web/static/src/js/views/basic/basic_renderer.js4
-rw-r--r--addons/web/static/src/js/views/list/list_renderer.js3
3 files changed, 93 insertions, 7 deletions
diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js
index ca0c7829a2c..b9a7d26e470 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -155,6 +155,10 @@ var BasicModel = AbstractModel.extend({
// save is performed.
this.mutex = new concurrency.Mutex();
+ // this array is used to accumulate RPC requests done in the same call
+ // stack, so that they can be batched in the minimum number of RPCs
+ this.batchedRPCsRequests = [];
+
this.localData = Object.create(null);
this._super.apply(this, arguments);
},
@@ -4071,6 +4075,86 @@ var BasicModel = AbstractModel.extend({
});
},
/**
+ * This function accumulates RPC requests done in the same call stack, and
+ * performs them in the next micro task tick so that similar requests can be
+ * batched in a single RPC.
+ *
+ * For now, only 'read' calls are supported.
+ *
+ * @private
+ * @param {Object} params
+ * @returns {Promise}
+ */
+ _performRPC: function (params) {
+ var self = this;
+
+ // save the RPC request
+ var request = _.extend({}, params);
+ var prom = new Promise(function (resolve, reject) {
+ request.resolve = resolve;
+ request.reject = reject;
+ });
+ this.batchedRPCsRequests.push(request);
+
+ // empty the pool of RPC requests in the next micro tick
+ Promise.resolve().then(function () {
+ if (!self.batchedRPCsRequests.length) {
+ // pool has already been processed
+ return;
+ }
+
+ // reset pool of RPC requests
+ var batchedRPCsRequests = self.batchedRPCsRequests;
+ self.batchedRPCsRequests = [];
+
+ // batch similar requests
+ var batches = {};
+ var key;
+ for (var i = 0; i < batchedRPCsRequests.length; i++) {
+ var request = batchedRPCsRequests[i];
+ key = request.model + ',' + JSON.stringify(request.context);
+ if (!batches[key]) {
+ batches[key] = _.extend({}, request, {requests: [request]});
+ } else {
+ batches[key].ids = _.uniq(batches[key].ids.concat(request.ids));
+ batches[key].fieldNames = _.uniq(batches[key].fieldNames.concat(request.fieldNames));
+ batches[key].requests.push(request);
+ }
+ }
+
+ // perform batched RPCs
+ function onSuccess(batch, results) {
+ for (var i = 0; i < batch.requests.length; i++) {
+ var request = batch.requests[i];
+ var fieldNames = request.fieldNames.concat(['id']);
+ var filteredResults = results.filter(function (record) {
+ return request.ids.indexOf(record.id) >= 0;
+ }).map(function (record) {
+ return _.pick(record, fieldNames);
+ });
+ request.resolve(filteredResults);
+ }
+ }
+ function onFailure(batch, error) {
+ for (var i = 0; i < batch.requests.length; i++) {
+ var request = batch.requests[i];
+ request.reject(error);
+ }
+ }
+ for (key in batches) {
+ var batch = batches[key];
+ self._rpc({
+ model: batch.model,
+ method: 'read',
+ args: [batch.ids, batch.fieldNames],
+ context: batch.context,
+ }).then(onSuccess.bind(null, batch)).guardedCatch(onFailure.bind(null, batch));
+ }
+ });
+
+ return prom;
+ },
+ /**
* Once a record is created and some data has been fetched, we need to do
* quite a lot of computations to determine what needs to be fetched. This
* method is doing that.
@@ -4277,11 +4361,12 @@ var BasicModel = AbstractModel.extend({
var def;
if (missingIDs.length && fieldNames.length) {
- def = self._rpc({
- model: list.model,
- method: 'read',
- args: [missingIDs, fieldNames],
+ def = self._performRPC({
context: list.getContext(),
+ fieldNames: fieldNames,
+ ids: missingIDs,
+ method: 'read',
+ model: list.model,
});
} else {
def = Promise.resolve(_.map(missingIDs, function (id) {
diff --git a/addons/web/static/src/js/views/basic/basic_renderer.js b/addons/web/static/src/js/views/basic/basic_renderer.js
index 6ffe35574bd..f73209a0330 100644
--- a/addons/web/static/src/js/views/basic/basic_renderer.js
+++ b/addons/web/static/src/js/views/basic/basic_renderer.js
@@ -651,8 +651,8 @@ var BasicRenderer = AbstractRenderer.extend({
// this.renderFieldWidget(...).addClass(...), the class is added on
// the temporary div and not on the actual element that will be
// rendered. As we do not return a promise and some callers cannot
- // wait for this.defs, we copy those attributes to the final element.
- widget.$el.attr($el.getAttributes());
+ // wait for this.defs, we copy those classnames to the final element.
+ widget.$el.addClass($el.attr('class'));
$el.replaceWith(widget.$el);
self._registerModifiers(node, record, widget, {
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 23a26bb6d73..ad1a95cd14f 100644
--- a/addons/web/static/src/js/views/list/list_renderer.js
+++ b/addons/web/static/src/js/views/list/list_renderer.js
@@ -405,7 +405,8 @@ var ListRenderer = BasicRenderer.extend({
isPassword: 'password' in node.attrs,
});
this._handleAttributes($td, node);
- return $td.html(formattedValue).attr('title', formattedValue);
+ var title = field.type !== 'boolean' ? formattedValue : '';
+ return $td.html(formattedValue).attr('title', title);
},
/**
* Renders the button element associated to the given node and record.