summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic/basic_model.js
diff options
context:
space:
mode:
authorRobot Odoo <xmo+roboto@odoo.com>2019-03-06 21:18:36 +0100
committerGitHub <noreply@github.com>2019-03-06 21:18:36 +0100
commitfc60c49ba3cb30fc4e436c9ffc1661d707fe858c (patch)
tree110bf47078ce4862cfad05aa061ba1238ebc99e6 /addons/web/static/src/js/views/basic/basic_model.js
parent414ac1a49a61e7f68b4c8dab8688a5b8840aefed (diff)
parent53e7050ce0c1bd873f239bce8ad81ba42c19d57a (diff)
[MERGE] web,*: update code base to jQuery 3.*
The goal of this task is to update jQuery from version 1.11 to 3.x. Task-id : 1896658 As we are skipping 2 magor versions, there are some breaking changes in the jquery library described here: https://jquery.com/upgrade-guide/3.0/ Main changes are: ------------------------- 1. Update the libraries jQuery, jQueryUI, QUnit and select2 to their latest version. The link to generate the build of jQueryUI with the used components: [jquery UI build](https://jqueryui.com/download/#!version=1.12.1&components=111110010101111110100010110000011010000000000000) 2. Use native promises ([a complete explanation about promises here](https://github.com/getify/You-Dont-Know-JS/blob/master/async%20%26%20performance/ch3.md)) everywhere possible: In order to [be A+ compliant](https://promisesaplus.com/), the jq3 deferred uses `setTimeout(..., 0)` for every then and catch, which takes between 4 and 10 ms by call. This would be way too slow to use in Odoo. To prevent this, we have used the native Promises everywhere possible. Be it with jq deferred or native promises, all promises are now asynchronous, this example will ALWAYS result in printing first "just after blabla" then "blabla" because the `then` function will always be asynchronous (opposite to what jquery used to do) ``` function () { Promise.resolve("blabla").then(console.log); console.log("just after blabla"); } // console: // just after blabla // blabla ``` 3. On native promises, you should see the catch like a try/catch in javascript, meaning that it will catch all errors including coding errors: For this we have introduced the concept of `guardedCatch`, which will be executed on promise rejection unless the promise is rejected because there is a code error or runtime error (calls on undefined, etc). 4. Native Promises do not have `done`, `fail` and `always` methods nor does it have the `state` method - `fail` can be replaced with `guardedCatch` - `return $.when(x).done(function() ...)` can be replaced with ``` var x = Promise.resolve(); // we get the x promise in a local variable, without then x.then(function() ...); // we add a new handler to x. This operation returns a new promise we don't care about return x; // we return the x promise that will be completed without waiting for the then ``` - `$.when().done(function ...)` without the return can be replaced by `then(function ....)`, because if it is not returned, it doesn't matter what is chained or not `$.when().always(function() ...)` can be replaced with ``` var always = function() ... Promise.resolve().then(always).guardedCatch(always); ``` - It is not possible to inspect the state of a promise, except in debug tools of chrome and firefox. Any code that relies on `$.Deferred.state()` should be rewritten to use flags in `then` and `catch` handlers. Remember that it is not possible to execute the handlers in a synchroneous manner, so when a promise is created, its `then` will never be called on the same execution stack. At best it will be called on the next microtask tick (before the next setTimeout). 5. We have introduced the concept of guardedCatch, to copy the behavior of the old fail: The guarded catch will only execute the handler in parameter if the rejection reason is not an `Error` ``` var failingPromise = Promise.reject({message:"I am the rejection reason"}); failingPromise.guardedCatch(function(reason) {console.log(reason);}); // is called, then returns a rejected promise failingPromise.catch(function(reason) {console.log(reason);}); // is called ``` will write the reason "I am the rejection reason" to the console twice: once because the `guardedCatch` will execute (and return a rejected `Promise` with the same reason it was called), a second time because the `catch` will catch the rejected reason returned by the `guardedCatch` ``` var promiseWithError = Promise.resolve().then(function() { a.b = 0; // <-- the variable a does not exist, so a.b is an error }); promiseWithError.guardedCatch(function(reason) {console.log(reason);}); // will not be called promiseWithError.catch(function(reason) {console.log(reason);}); // will be called ``` contrary to how deferred used to work, here the catch will be called because it behaves like a `try... catch...` and will catch the coding error like if it was a business error. The `guardedCatch` however does not call the handler if the rejection reason is an error. In all Odoo code, we never reject a promise with an error and always use objects to defined the rejected reason, so it is a good idea to use `guardedCatch` everywhere. 6. Native Promises can have only one parameter passed to a `then` function or a `catch` function: `xxx.then(function(param1, param2) {...}); // param2 will never have a value` - we can replace `$.when(prom1, prom2).then(function(result1, result2) { ... });` by ``` Promise.all([prom1, prom2]).then(function(results) { var result1 = results[1]; var result2 = results[2]; ... }); ``` 7. We are now using ES7 code in the unit tests (async await). closes odoo/odoo#28457 Signed-off-by: Aaron Bohy (aab) <aab@odoo.com> Co-authored-by: Aaron Bohy <aab@odoo.com> Co-authored-by: Christophe Matthieu <chm@odoo.com> Co-authored-by: Mathieu Duckerts-Antoine <dam@odoo.com> Co-authored-by: David Monjoie <dmo@odoo.com> Co-authored-by: Martin Geubelle <mge@odoo.com> Co-authored-by: svs-odoo <svs@odoo.com> Co-authored-by: Vincent Schippefilt <vsc@odoo.com>
Diffstat (limited to 'addons/web/static/src/js/views/basic/basic_model.js')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js417
1 files changed, 213 insertions, 204 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 4d86ef4293b..95b6d439ec1 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -173,7 +173,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} [options]
* @param {string} [options.position=top] if the new record should be added
* on top or on bottom of the list
- * @returns {Deferred<string>} resolves to the id of the new created record
+ * @returns {Promise<string>} resolves to the id of the new created record
*/
addDefaultRecord: function (listID, options) {
var self = this;
@@ -241,7 +241,7 @@ var BasicModel = AbstractModel.extend({
* assume main viewType from the record
* @param {Array} [options.fieldNames] list of field names for which a
* default value must be generated (used to complete the values dict)
- * @returns {Deferred}
+ * @returns {Promise}
*/
applyDefaultValues: function (recordID, values, options) {
options = options || {};
@@ -304,7 +304,7 @@ var BasicModel = AbstractModel.extend({
}
}
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Onchange RPCs may return values for fields that are not in the current
@@ -321,7 +321,7 @@ var BasicModel = AbstractModel.extend({
*
* @param {string} recordID local resource id of a record
* @param {string} viewType the current viewType
- * @returns {Deferred<string>} resolves to the id of the record
+ * @returns {Promise<string>} resolves to the id of the record
*/
applyRawChanges: function (recordID, viewType) {
var record = this.localData[recordID];
@@ -388,7 +388,7 @@ var BasicModel = AbstractModel.extend({
* @param {string[]} recordIds list of local resources ids. They should all
* be of type 'record', be of the same model and have the same parent.
* @param {string} modelName mode name used to unlink the records
- * @returns {Deferred}
+ * @returns {Promise}
*/
deleteRecords: function (recordIds, modelName) {
var self = this;
@@ -457,7 +457,7 @@ var BasicModel = AbstractModel.extend({
* Duplicate a record (by calling the 'copy' route)
*
* @param {string} recordID id for a local resource
- * @returns {Deferred<string>} resolves to the id of duplicate record
+ * @returns {Promise<string>} resolves to the id of duplicate record
*/
duplicateRecord: function (recordID) {
var self = this;
@@ -725,7 +725,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} params.fields contains the description of each field
* @param {string} [params.type] 'record' or 'list'
* @param {string} [params.recordID] an ID for an existing resource.
- * @returns {Deferred<string>} resolves to a local id, or handle
+ * @returns {Promise<string>} resolves to a local id, or handle
*/
load: function (params) {
params.type = params.type || (params.res_id !== undefined ? 'record' : 'list');
@@ -758,7 +758,7 @@ var BasicModel = AbstractModel.extend({
* @param {string} model name of the model
* @param {Object[]} fields a description of field properties
* @param {Object} [fieldInfo] various field info that we want to set
- * @returns {string} the local id for the created resource
+ * @returns {Promise<string>} the local id for the created resource
*/
makeRecord: function (model, fields, fieldInfo) {
var self = this;
@@ -849,7 +849,7 @@ var BasicModel = AbstractModel.extend({
record.data[field.name] = field.value;
}
});
- return $.when.apply($, defs).then(function () {
+ return Promise.all(defs).then(function () {
return record.id;
});
},
@@ -863,7 +863,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} changes a map field => new value
* @param {Object} [options] will be transferred to the applyChange method
* @see _applyChange
- * @returns {string[]} list of changed fields
+ * @returns {Promise<string[]>} list of changed fields
*/
notifyChanges: function (record_id, changes, options) {
return this.mutex.exec(this._applyChange.bind(this, record_id, changes, options));
@@ -876,7 +876,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} [options]
* @param {boolean} [options.keepChanges=false] if true, doesn't discard the
* changes on the record before reloading it
- * @returns {Deferred<string>} resolves to the id of the resource
+ * @returns {Promise<string>} resolves to the id of the resource
*/
reload: function (id, options) {
return this.mutex.exec(this._reload.bind(this, id, options));
@@ -925,12 +925,12 @@ var BasicModel = AbstractModel.extend({
* @param {object} [options]
* @param {integer} [options.offset]
* @param {string} [options.field] the field name used as sequence
- * @returns {Deferred<string>} resolves to the local id of the parent
+ * @returns {Promise<string>} resolves to the local id of the parent
*/
resequence: function (modelName, resIDs, parentID, options) {
options = options || {};
if ((resIDs.length <= 1)) {
- return $.when(parentID); // there is nothing to sort
+ return Promise.resolve(parentID); // there is nothing to sort
}
var self = this;
var data = this.localData[parentID];
@@ -952,7 +952,7 @@ var BasicModel = AbstractModel.extend({
if (!wasResequenced) {
// the field on which the resequence was triggered does not
// exist, so no resequence happened server-side
- return $.when();
+ return Promise.resolve();
}
var field = params.field ? params.field : 'sequence';
@@ -1010,7 +1010,7 @@ var BasicModel = AbstractModel.extend({
* be restored later by call discardChanges with option rollback to true
* @param {string} [options.viewType] current viewType. If not set, we will
* assume main viewType from the record
- * @returns {Deferred}
+ * @returns {Promise}
* Resolved with the list of field names (whose value has been modified)
*/
save: function (recordID, options) {
@@ -1054,57 +1054,59 @@ var BasicModel = AbstractModel.extend({
});
}
- var def = $.Deferred();
- var changedFields = Object.keys(changes);
+ var prom = new Promise(function (resolve, reject) {
+ var changedFields = Object.keys(changes);
- if (options.savePoint) {
- return def.resolve(changedFields);
- }
+ if (options.savePoint) {
+ resolve(changedFields);
+ return;
+ }
- def.then(function () {
- record._isDirty = false;
- });
- // in the case of a write, only perform the RPC if there are changes to save
- if (method === 'create' || changedFields.length) {
- var args = method === 'write' ? [[record.data.id], changes] : [changes];
- self._rpc({
- model: record.model,
- method: method,
- args: args,
- context: record.getContext(),
- }).then(function (id) {
- if (method === 'create') {
- record.res_id = id; // create returns an id, write returns a boolean
- record.data.id = id;
- record.offset = record.res_ids.length;
- record.res_ids.push(id);
- record.count++;
- }
+ // in the case of a write, only perform the RPC if there are changes to save
+ if (method === 'create' || changedFields.length) {
+ var args = method === 'write' ? [[record.data.id], changes] : [changes];
+ self._rpc({
+ model: record.model,
+ method: method,
+ args: args,
+ context: record.getContext(),
+ }).then(function (id) {
+ if (method === 'create') {
+ record.res_id = id; // create returns an id, write returns a boolean
+ record.data.id = id;
+ record.offset = record.res_ids.length;
+ record.res_ids.push(id);
+ record.count++;
+ }
- var _changes = record._changes;
+ var _changes = record._changes;
- // Erase changes as they have been applied
- record._changes = {};
+ // Erase changes as they have been applied
+ record._changes = {};
- // Optionally clear the DataManager's cache
- self._invalidateCache(record);
+ // Optionally clear the DataManager's cache
+ self._invalidateCache(record);
- self.unfreezeOrder(record.id);
+ self.unfreezeOrder(record.id);
- // Update the data directly or reload them
- if (shouldReload) {
- self._fetchRecord(record).then(function () {
- def.resolve(changedFields);
- });
- } else {
- _.extend(record.data, _changes);
- def.resolve(changedFields);
- }
- }).fail(def.reject.bind(def));
- } else {
- def.resolve(changedFields);
- }
- return def;
+ // Update the data directly or reload them
+ if (shouldReload) {
+ self._fetchRecord(record).then(function () {
+ resolve(changedFields);
+ });
+ } else {
+ _.extend(record.data, _changes);
+ resolve(changedFields);
+ }
+ }).guardedCatch(reject);
+ } else {
+ resolve(changedFields);
+ }
+ });
+ prom.then(function () {
+ record._isDirty = false;
+ });
+ return prom;
});
},
/**
@@ -1123,7 +1125,7 @@ var BasicModel = AbstractModel.extend({
*
* @param {string} list_id id for the list resource
* @param {string} fieldName valid field name
- * @returns {Deferred}
+ * @returns {Promise}
*/
setSort: function (list_id, fieldName) {
var list = this.localData[list_id];
@@ -1153,7 +1155,7 @@ var BasicModel = AbstractModel.extend({
// sort field is still unknown (i.e. on other pages for example)
return this._fetchUngroupedList(list);
}
- return $.when();
+ return Promise.resolve();
},
/**
* Toggle the active value of given records (to archive/unarchive them)
@@ -1161,7 +1163,7 @@ var BasicModel = AbstractModel.extend({
* @param {Array} recordIDs local ids of the records to (un)archive
* @param {boolean} value false to archive, true to unarchive (value of the active field)
* @param {string} parentID id of the parent resource to reload
- * @returns {Deferred<string>} resolves to the parent id
+ * @returns {Promise<string>} resolves to the parent id
*/
toggleActive: function (recordIDs, value, parentID) {
var self = this;
@@ -1185,7 +1187,7 @@ var BasicModel = AbstractModel.extend({
* data
*
* @param {string} groupId
- * @returns {Deferred<string>} resolves to the group id
+ * @returns {Promise<string>} resolves to the group id
*/
toggleGroup: function (groupId) {
var self = this;
@@ -1196,7 +1198,7 @@ var BasicModel = AbstractModel.extend({
group.res_ids = [];
group.offset = 0;
this._updateParentResIDs(group);
- return $.when(groupId);
+ return Promise.resolve(groupId);
}
if (!group.isOpen) {
group.isOpen = true;
@@ -1206,7 +1208,7 @@ var BasicModel = AbstractModel.extend({
self._updateParentResIDs(group);
});
}
- return $.when(def).then(function () {
+ return Promise.resolve(def).then(function () {
return groupId;
});
}
@@ -1260,7 +1262,7 @@ var BasicModel = AbstractModel.extend({
* If several contexts are found, multiple records are added
* @param {boolean} [options.allowWarning=false] if true, the default record
* operation can complete, even if a warning is raised
- * @returns {Deferred<[string]>} resolves to the new records ids
+ * @returns {Promise<[string]>} resolves to the new records ids
*/
_addX2ManyDefaultRecord: function (list, options) {
var self = this;
@@ -1286,9 +1288,9 @@ var BasicModel = AbstractModel.extend({
makeDefaultRecords.push(self._makeDefaultRecord(list.model, params));
}
- return $.when.apply($, makeDefaultRecords).then(function (){
+ return Promise.all(makeDefaultRecords).then(function (resultIds){
var ids = [];
- _.each(arguments, function (id){
+ _.each(resultIds, function (id){
ids.push(id);
list._changes.push({operation: 'ADD', id: id, position: position, isNew: true});
@@ -1324,7 +1326,7 @@ var BasicModel = AbstractModel.extend({
* @param {boolean} [options.allowWarning=false] if true, change
* operation can complete, even if a warning is raised
* (only supported by X2ManyChange)
- * @returns {Deferred} list of changed fields
+ * @returns {Promise} list of changed fields
*/
_applyChange: function (recordID, changes, options) {
var self = this;
@@ -1354,10 +1356,10 @@ var BasicModel = AbstractModel.extend({
}
if (options.notifyChange === false) {
- return $.Deferred().resolve(_.keys(changes));
+ return Promise.resolve(_.keys(changes));
}
- return $.when.apply($, defs).then(function () {
+ return Promise.all(defs).then(function () {
var onChangeFields = []; // the fields that have changed and that have an on_change
for (var fieldName in changes) {
field = record.fields[fieldName];
@@ -1368,22 +1370,22 @@ var BasicModel = AbstractModel.extend({
}
}
}
- var onchangeDef = $.Deferred();
- if (onChangeFields.length) {
- self._performOnChange(record, onChangeFields, options.viewType)
+ return new Promise(function (resolve, reject) {
+ if (onChangeFields.length) {
+ self._performOnChange(record, onChangeFields, options.viewType)
.then(function (result) {
delete record._warning;
- onchangeDef.resolve(_.keys(changes).concat(Object.keys(result && result.value || {})));
- }).fail(function () {
+ resolve(_.keys(changes).concat(Object.keys(result && result.value || {})));
+ }).guardedCatch(function () {
self._visitChildren(record, function (elem) {
_.extend(elem, initialData[elem.id]);
});
- onchangeDef.resolve({});
+ resolve({});
});
- } else {
- onchangeDef = $.Deferred().resolve(_.keys(changes));
- }
- return onchangeDef.then(function (fieldNames) {
+ } else {
+ resolve(_.keys(changes));
+ }
+ }).then(function (fieldNames) {
_.each(fieldNames, function (name) {
if (record._changes && record._changes[name] === record.data[name]) {
delete record._changes[name];
@@ -1407,13 +1409,13 @@ var BasicModel = AbstractModel.extend({
* @param {Object} record
* @param {string} fieldName
* @param {Object} [data]
- * @returns {Deferred}
+ * @returns {Promise}
*/
_applyX2OneChange: function (record, fieldName, data) {
var self = this;
if (!data || !data.id) {
record._changes[fieldName] = false;
- return $.when();
+ return Promise.resolve();
}
// here, we check that the many2one really changed. If the res_id is the
@@ -1429,7 +1431,7 @@ var BasicModel = AbstractModel.extend({
}
var relatedRecord = this.localData[relatedID];
if (relatedRecord && (data.id === this.localData[relatedID].res_id)) {
- return $.when();
+ return Promise.resolve();
}
var rel_data = _.pick(data, 'id', 'display_name');
var field = record.fields[fieldName];
@@ -1450,7 +1452,7 @@ var BasicModel = AbstractModel.extend({
rel_data.display_name = result[0][1];
});
}
- return $.when(def).then(function () {
+ return Promise.resolve(def).then(function () {
var rec = self._makeDataPoint({
context: record.context,
data: rel_data,
@@ -1471,7 +1473,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} record
* @param {string} [viewType] current viewType. If not set, we will assume
* main viewType from the record
- * @returns {Deferred}
+ * @returns {Promise}
*/
_applyOnChange: function (values, record, viewType) {
var self = this;
@@ -1634,7 +1636,7 @@ var BasicModel = AbstractModel.extend({
var def = self._readUngroupedList(list).then(function () {
var x2ManysDef = self._fetchX2ManysBatched(list);
var referencesDef = self._fetchReferencesBatched(list);
- return $.when(x2ManysDef, referencesDef);
+ return Promise.all([x2ManysDef, referencesDef]);
});
defs.push(def);
} else {
@@ -1644,7 +1646,7 @@ var BasicModel = AbstractModel.extend({
}
}
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
// inner function that adds a record (based on its res_id) to a list
// dataPoint (used for onchanges that return commands 4 (LINK TO) or
@@ -1680,7 +1682,7 @@ var BasicModel = AbstractModel.extend({
* @param {boolean} [allowWarning=false] if true, change
* operation can complete, even if a warning is raised
* (only supported by the 'CREATE' command.operation)
- * @returns {Deferred}
+ * @returns {Promise}
*/
_applyX2ManyChange: function (record, fieldName, command, viewType, allowWarning) {
if (command.operation === 'TRIGGER_ONCHANGE') {
@@ -1688,7 +1690,7 @@ var BasicModel = AbstractModel.extend({
// there is no need to apply any change on the record (the changes
// have probably been already applied and saved, usecase: many2many
// edition in a dialog)
- return $.when();
+ return Promise.resolve();
}
var self = this;
@@ -1765,10 +1767,10 @@ var BasicModel = AbstractModel.extend({
list_records[record.id].data = record;
self._parseServerData(fieldNames, list, record);
});
- return $.when(
+ return Promise.all([
self._fetchX2ManysBatched(list),
self._fetchReferencesBatched(list)
- );
+ ]);
});
defs.push(def);
}
@@ -1850,10 +1852,10 @@ var BasicModel = AbstractModel.extend({
}),
}, viewType);
}
- return $.when(addDef, removedDef);
+ return Promise.all([addDef, removedDef]);
}
- return $.when.apply($, defs).then(function () {
+ return Promise.all(defs).then(function () {
// ensure to fetch up to 'limit' records (may be useful if records of
// the current page have been removed)
return self._readUngroupedList(list).then(function () {
@@ -2103,7 +2105,7 @@ var BasicModel = AbstractModel.extend({
* Fetch all name_gets for the many2ones in a group
*
* @param {Object[]} groups a list of object with context and record sub keys
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchMany2OneGroup: function (groups) {
var ids = _.uniq(_.pluck(_.pluck(groups, 'record'), 'res_id'));
@@ -2124,7 +2126,7 @@ var BasicModel = AbstractModel.extend({
* Fetch name_get for a record datapoint.
*
* @param {Object} dataPoint
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchNameGet: function (dataPoint) {
return this._rpc({
@@ -2143,7 +2145,7 @@ var BasicModel = AbstractModel.extend({
* @params {Object} list: must be a datapoint of type list
* (for example: a datapoint representing a x2many)
* @params {string} fieldName: the name of a field of type Many2one or Reference
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchNameGets: function (list, fieldName) {
var self = this;
@@ -2168,7 +2170,7 @@ var BasicModel = AbstractModel.extend({
});
if (!ids.length) {
- return $.when();
+ return Promise.resolve();
}
return this._rpc({
model: model,
@@ -2195,7 +2197,7 @@ var BasicModel = AbstractModel.extend({
* @param {string} [options.viewType] the type of view for which the record
* is fetched (usefull to load the adequate fields), by defaults, uses
* record.viewType
- * @returns {Deferred<Object>} resolves to the record or is rejected in
+ * @returns {Promise<Object>} resolves to the record or is rejected in
* case no id given were valid ids
*/
_fetchRecord: function (record, options) {
@@ -2211,7 +2213,7 @@ var BasicModel = AbstractModel.extend({
})
.then(function (result) {
if (result.length === 0) {
- return $.Deferred().reject();
+ return Promise.reject();
}
result = result[0];
record.data = _.extend({}, record.data, result);
@@ -2220,10 +2222,10 @@ var BasicModel = AbstractModel.extend({
self._parseServerData(fieldNames, record, record.data);
})
.then(function () {
- return $.when(
+ return Promise.all([
self._fetchX2Manys(record, options),
self._fetchReferences(record, options)
- ).then(function () {
+ ]).then(function () {
return self._postprocess(record, options);
});
});
@@ -2234,7 +2236,7 @@ var BasicModel = AbstractModel.extend({
* @private
* @param {Object} record
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReference: function (record, fieldName) {
var self = this;
@@ -2259,7 +2261,7 @@ var BasicModel = AbstractModel.extend({
});
});
}
- return $.when(def);
+ return Promise.resolve(def);
},
/**
* Fetches data for reference fields and assigns these data to newly
@@ -2270,7 +2272,7 @@ var BasicModel = AbstractModel.extend({
* @see _getDataToFetchByModel
* @param {string} model
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReferenceData: function (datapoints, model, fieldName) {
var self = this;
@@ -2307,7 +2309,7 @@ var BasicModel = AbstractModel.extend({
*
* @private
* @param {Object} record
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReferences: function (record, options) {
var self = this;
@@ -2324,7 +2326,7 @@ var BasicModel = AbstractModel.extend({
defs.push(def);
}
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Batch requests for one reference field in list (one request by different
@@ -2333,7 +2335,7 @@ var BasicModel = AbstractModel.extend({
* @see _fetchReferencesBatched
* @param {Object} list
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReferenceBatched: function (list, fieldName) {
var self = this;
@@ -2347,13 +2349,13 @@ var BasicModel = AbstractModel.extend({
defs.push(self._fetchReferenceData(datapoints, model, fieldName));
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Batch requests for references for datapoint of type list.
*
* @param {Object} list
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReferencesBatched: function (list) {
var defs = [];
@@ -2364,7 +2366,7 @@ var BasicModel = AbstractModel.extend({
defs.push(this._fetchReferenceBatched(list, fieldNames[i]));
}
}
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Batch reference requests for all records in list.
@@ -2372,7 +2374,7 @@ var BasicModel = AbstractModel.extend({
* @see _fetchReferencesSingleBatch
* @param {Object} list a valid resource object
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReferenceSingleBatch: function (list, fieldName) {
var self = this;
@@ -2390,14 +2392,14 @@ var BasicModel = AbstractModel.extend({
defs.push(self._fetchReferenceData(datapoints, model, fieldName));
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Batch requests for all reference field in list's children.
* Called by _readGroup to make only one 'name_get' rpc by fieldName.
*
* @param {Object} list a valid resource object
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchReferencesSingleBatch: function (list) {
var defs = [];
@@ -2408,7 +2410,7 @@ var BasicModel = AbstractModel.extend({
defs.push(this._fetchReferenceSingleBatch(list, fieldNames[fIndex]));
}
}
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Fetch model data from server, relationally to fieldName and resulted
@@ -2420,7 +2422,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} toFetch a list of records and res_ids,
* @see _getDataToFetch
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchRelatedData: function (list, toFetch, fieldName) {
var self = this;
@@ -2431,7 +2433,7 @@ var BasicModel = AbstractModel.extend({
var fieldInfo = list.fieldsInfo[list.viewType][fieldName];
if (!ids.length || fieldInfo.__no_fetch) {
- return $.when();
+ return Promise.resolve();
}
var def;
@@ -2445,7 +2447,7 @@ var BasicModel = AbstractModel.extend({
context: list.getContext() || {},
});
} else {
- def = $.when(_.map(ids, function (id) {
+ def = Promise.resolve(_.map(ids, function (id) {
return {id:id};
}));
}
@@ -2465,10 +2467,10 @@ var BasicModel = AbstractModel.extend({
* This method is currently only called by _makeDefaultRecord, it should be
* called by the onchange methods at some point.
*
- * @todo fix bug: returns a list of deferred, not a deferred
+ * @todo fix bug: returns a list of promise, not a promise
*
* @param {Object} record a valid resource object
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchRelationalData: function (record) {
var self = this;
@@ -2496,7 +2498,7 @@ var BasicModel = AbstractModel.extend({
return [elem.record.model, JSON.stringify(elem.context)].join();
});
- return $.when.apply($, _.map(groups, this._fetchMany2OneGroup.bind(this)));
+ return Promise.all(_.map(groups, this._fetchMany2OneGroup.bind(this)));
},
/**
* Check the AbstractField specializations that are (will be) used by the
@@ -2507,15 +2509,15 @@ var BasicModel = AbstractModel.extend({
*
* @param {Object} record - an element from the localData
* @param {Object} options
- * @returns {Deferred<Array>}
- * The deferred is resolved with an array containing the names of
+ * @returns {Promise<Array>}
+ * The promise is resolved with an array containing the names of
* the field whose special data has been changed.
*/
_fetchSpecialData: function (record, options) {
var self = this;
var specialFieldNames = [];
var fieldNames = (options && options.fieldNames) || record.getFieldNames();
- return $.when.apply($, _.map(fieldNames, function (name) {
+ return Promise.all(_.map(fieldNames, function (name) {
var viewType = (options && options.viewType) || record.viewType;
var fieldInfo = record.fieldsInfo[viewType][name] || {};
var Widget = fieldInfo.Widget;
@@ -2541,16 +2543,16 @@ var BasicModel = AbstractModel.extend({
* @param {Object} fieldInfo
* @param {string[]} [fieldsToRead] - the m2os fields to read (id and
* display_name are automatic).
- * @returns {Deferred<any>}
- * The deferred is resolved with the fetched special data. If this
+ * @returns {Promise<any>}
+ * The promise is resolved with the fetched special data. If this
* data is the same as the previously fetched one (for the given
- * parameters), no RPC is done and the deferred is resolved with
+ * parameters), no RPC is done and the promise is resolved with
* the undefined value.
*/
_fetchSpecialMany2ones: function (record, fieldName, fieldInfo, fieldsToRead) {
var field = record.fields[fieldName];
if (field.type !== "many2one") {
- return $.when();
+ return Promise.resolve();
}
var context = record.getContext({fieldName: fieldName});
@@ -2571,7 +2573,7 @@ var BasicModel = AbstractModel.extend({
domain: domain,
});
if (!hasChanged) {
- return $.when();
+ return Promise.resolve();
}
var self = this;
@@ -2607,16 +2609,16 @@ var BasicModel = AbstractModel.extend({
*
* @param {Object} record - an element from the localData
* @param {Object} fieldName - the name of the field
- * @returns {Deferred<any>}
- * The deferred is resolved with the fetched special data. If this
+ * @returns {Promise<any>}
+ * The promise is resolved with the fetched special data. If this
* data is the same as the previously fetched one (for the given
- * parameters), no RPC is done and the deferred is resolved with
+ * parameters), no RPC is done and the promise is resolved with
* the undefined value.
*/
_fetchSpecialRelation: function (record, fieldName) {
var field = record.fields[fieldName];
if (!_.contains(["many2one", "many2many", "one2many"], field.type)) {
- return $.when();
+ return Promise.resolve();
}
var context = record.getContext({fieldName: fieldName});
@@ -2628,7 +2630,7 @@ var BasicModel = AbstractModel.extend({
domain: domain,
});
if (!hasChanged) {
- return $.when();
+ return Promise.resolve();
}
return this._rpc({
@@ -2645,7 +2647,7 @@ var BasicModel = AbstractModel.extend({
* @private
* @param {Object} record - an element from the localData
* @param {Object} fieldName - the name of the field
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchSpecialReference: function (record, fieldName) {
var def;
@@ -2655,7 +2657,7 @@ var BasicModel = AbstractModel.extend({
// needs to be fetched a posteriori
def = this._fetchReference(record, fieldName);
}
- return $.when(def);
+ return Promise.resolve(def);
},
/**
* Fetches all the m2o records associated to the given fieldName. If the
@@ -2666,10 +2668,10 @@ var BasicModel = AbstractModel.extend({
* @param {Object} record - an element from the localData
* @param {Object} fieldName - the name of the field
* @param {Object} fieldInfo
- * @returns {Deferred<any>}
- * The deferred is resolved with the fetched special data. If this
+ * @returns {Promise<any>}
+ * The promise is resolved with the fetched special data. If this
* data is the same as the previously fetched one (for the given
- * parameters), no RPC is done and the deferred is resolved with
+ * parameters), no RPC is done and the promise is resolved with
* the undefined value.
*/
_fetchSpecialStatus: function (record, fieldName, fieldInfo) {
@@ -2689,13 +2691,14 @@ var BasicModel = AbstractModel.extend({
* @param {Object} record - an element from the localData
* @param {Object} fieldName - the name of the field
* @param {Object} fieldInfo
- * @returns {Deferred<any>}
- * The deferred is resolved with the fetched special data. If this
+ * @returns {Promise<any>}
+ * The promise is resolved with the fetched special data. If this
* data is the same as the previously fetched one (for the given
- * parameters), no RPC is done and the deferred is resolved with
+ * parameters), no RPC is done and the promise is resolved with
* the undefined value.
*/
_fetchSpecialDomain: function (record, fieldName, fieldInfo) {
+ var self = this;
var context = record.getContext({fieldName: fieldName});
var domainModel = fieldInfo.options.model;
@@ -2711,34 +2714,37 @@ var BasicModel = AbstractModel.extend({
domainValue: domainValue,
});
if (!hasChanged) {
- return $.when();
+ return Promise.resolve();
} else if (!domainModel) {
- return $.when({
+ return Promise.resolve({
model: domainModel,
nbRecords: 0,
});
}
- var def = $.Deferred();
- var evalContext = this._getEvalContext(record);
- this._rpc({
+ return new Promise(function (resolve) {
+ var evalContext = self._getEvalContext(record);
+ self._rpc({
model: domainModel,
method: 'search_count',
args: [Domain.prototype.stringToArray(domainValue, evalContext)],
context: context
})
- .then(_.identity, function (error, e) {
- e.preventDefault(); // prevent traceback (the search_count might be intended to break)
- return false;
- })
- .always(function (nbRecords) {
- def.resolve({
+ .then(function (nbRecords) {
+ resolve({
model: domainModel,
nbRecords: nbRecords,
});
+ })
+ .guardedCatch(function (reason) {
+ var e = reason.event;
+ e.preventDefault(); // prevent traceback (the search_count might be intended to break)
+ resolve({
+ model: domainModel,
+ nbRecords: 0,
+ });
});
-
- return def;
+ });
},
/**
* Fetch all data in a ungrouped list
@@ -2748,7 +2754,7 @@ var BasicModel = AbstractModel.extend({
* @param {boolean} [options.enableRelationalFetch=true] if false, will not
* fetch x2m and relational data (that will be done by _readGroup in this
* case).
- * @returns {Deferred<Object>} resolves to the fetched list
+ * @returns {Promise<Object>} resolves to the fecthed list
*/
_fetchUngroupedList: function (list, options) {
options = _.defaults(options || {}, {enableRelationalFetch: true});
@@ -2762,7 +2768,7 @@ var BasicModel = AbstractModel.extend({
var defs = _.map(many2ones, function (name) {
return self._fetchNameGets(list, name);
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
}
});
} else {
@@ -2770,10 +2776,10 @@ var BasicModel = AbstractModel.extend({
}
return def.then(function () {
if (options.enableRelationalFetch) {
- return $.when(
- self._fetchX2ManysBatched(list, options),
- self._fetchReferencesBatched(list, options)
- );
+ return Promise.all([
+ self._fetchX2ManysBatched(list),
+ self._fetchReferencesBatched(list)
+ ]);
}
}).then(function () {
return list;
@@ -2785,7 +2791,7 @@ var BasicModel = AbstractModel.extend({
* @see _fetchX2ManysBatched
* @param {Object} list
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchX2ManyBatched: function (list, fieldName) {
list = this._applyX2ManyOperations(list);
@@ -2810,7 +2816,7 @@ var BasicModel = AbstractModel.extend({
* @param {string} [options.viewType] the type of view for which the main
* record is fetched (useful to load the adequate fields), by defaults,
* uses record.viewType
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchX2Manys: function (record, options) {
var self = this;
@@ -2845,22 +2851,22 @@ var BasicModel = AbstractModel.extend({
record.data[fieldName] = list.id;
if (!fieldInfo.__no_fetch) {
var def = self._readUngroupedList(list).then(function () {
- return $.when(
+ return Promise.all([
self._fetchX2ManysBatched(list),
self._fetchReferencesBatched(list)
- );
+ ]);
});
defs.push(def);
}
}
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* batch request for x2ms for datapoint of type list
*
* @param {Object} list
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchX2ManysBatched: function (list) {
var defs = [];
@@ -2871,7 +2877,7 @@ var BasicModel = AbstractModel.extend({
defs.push(this._fetchX2ManyBatched(list, fieldNames[i]));
}
}
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* For a non-static list, batches requests for all its sublists' records.
@@ -2881,7 +2887,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} list a valid resource object, its data must be another
* list containing records
* @param {string} fieldName
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchX2ManySingleBatch: function (list, fieldName) {
var self = this;
@@ -2904,7 +2910,7 @@ var BasicModel = AbstractModel.extend({
* Called by _readGroup to make only one 'read' rpc by fieldName.
*
* @param {Object} list a valid resource object
- * @returns {Deferred}
+ * @returns {Promise}
*/
_fetchX2ManysSingleBatch: function (list) {
var defs = [];
@@ -2915,7 +2921,7 @@ var BasicModel = AbstractModel.extend({
defs.push(this._fetchX2ManySingleBatch(list, fieldNames[i]));
}
}
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Generates an object mapping field names to their changed value in a given
@@ -3584,12 +3590,12 @@ var BasicModel = AbstractModel.extend({
* @param {boolean} [options.keepEmptyGroups=false] if set, the groups not
* present in the read_group anymore (empty groups) will stay in the
* datapoint (used to mimic the kanban renderer behaviour for example)
- * @returns {Deferred}
+ * @returns {Promise}
*/
_load: function (dataPoint, options) {
if (options && options.onlyGroups &&
!(dataPoint.type === 'list' && dataPoint.groupedBy.length)) {
- return $.when(dataPoint);
+ return Promise.resolve(dataPoint);
}
if (dataPoint.type === 'record') {
@@ -3733,7 +3739,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} params.fields contains the description of each field
* @param {Object} params.context the context for the new record
* @param {string} params.viewType the key in fieldsInfo of the fields to load
- * @returns {Deferred<string>} resolves to the id for the created resource
+ * @returns {Promise<string>} resolves to the id for the created resource
*/
_makeDefaultRecord: function (modelName, params) {
var self = this;
@@ -3787,16 +3793,19 @@ var BasicModel = AbstractModel.extend({
return self.applyDefaultValues(record.id, result, {fieldNames: fieldNames})
.then(function () {
- var def = $.Deferred();
- self._performOnChange(record, fields_key).always(function () {
- if (record._warning) {
- if (params.allowWarning) {
- delete record._warning;
- } else {
- def.reject();
+ var def = new Promise(function (resolve, reject) {
+ var always = function () {
+ if (record._warning) {
+ if (params.allowWarning) {
+ delete record._warning;
+ } else {
+ reject();
+ }
}
- }
- def.resolve();
+ resolve();
+ };
+ self._performOnChange(record, fields_key)
+ .then(always).guardedCatch(always);
});
return def;
})
@@ -3865,13 +3874,13 @@ var BasicModel = AbstractModel.extend({
* @param {string[]} fields changed fields
* @param {string} [viewType] current viewType. If not set, we will assume
* main viewType from the record
- * @returns {Deferred}
+ * @returns {Promise}
*/
_performOnChange: function (record, fields, viewType) {
var self = this;
var onchangeSpec = this._buildOnchangeSpecs(record, viewType);
if (!onchangeSpec) {
- return $.when();
+ return Promise.resolve();
}
var idList = record.data.id ? [record.data.id] : [];
var options = {
@@ -3925,7 +3934,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} [options]
* @param {Object} [options.viewType] current viewType. If not set, we will
* assume main viewType from the record
- * @returns {Deferred<Object>} resolves to the finished resource
+ * @returns {Promise<Object>} resolves to the finished resource
*/
_postprocess: function (record, options) {
var self = this;
@@ -3954,7 +3963,7 @@ var BasicModel = AbstractModel.extend({
defs.push(this._fetchSpecialData(record, options));
- return $.when.apply($, defs).then(function () {
+ return Promise.all(defs).then(function () {
return record;
});
},
@@ -3975,7 +3984,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} [options]
* @param {string} [options.viewType] current viewType. If not set, we will
* assume main viewType from the record
- * @returns {Deferred}
+ * @returns {Promise}
*/
_processX2ManyCommands: function (record, fieldName, commands, options) {
var self = this;
@@ -4076,10 +4085,10 @@ var BasicModel = AbstractModel.extend({
x2manyList._changes.push({operation: 'ADD', resID: res_id});
});
var def = self._readUngroupedList(x2manyList).then(function () {
- return $.when(
+ return Promise.all([
self._fetchX2ManysBatched(x2manyList),
self._fetchReferencesBatched(x2manyList)
- );
+ ]);
});
defs.push(def);
}
@@ -4090,7 +4099,7 @@ var BasicModel = AbstractModel.extend({
defs.push(self._fetchNameGets(x2manyList, name));
});
- return $.when.apply($, defs);
+ return Promise.all(defs);
},
/**
* Reads data from server for all missing fields.
@@ -4099,7 +4108,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} list a valid resource object
* @param {interger[]} resIDs
* @param {string[]} fieldNames to check and read if missing
- * @returns {Deferred<Object>}
+ * @returns {Promise<Object>}
*/
_readMissingFields: function (list, resIDs, fieldNames) {
var self = this;
@@ -4128,7 +4137,7 @@ var BasicModel = AbstractModel.extend({
context: list.getContext(),
});
} else {
- def = $.when(_.map(missingIDs, function (id) {
+ def = Promise.resolve(_.map(missingIDs, function (id) {
return {id:id};
}));
}
@@ -4174,7 +4183,7 @@ var BasicModel = AbstractModel.extend({
*
* @param {Object} list valid resource object
* @param {Object} [options] @see _load
- * @returns {Deferred<Object>} resolves to the fetched group object
+ * @returns {Promise<Object>} resolves to the fetched group object
*/
_readGroup: function (list, options) {
var self = this;
@@ -4285,20 +4294,20 @@ var BasicModel = AbstractModel.extend({
});
}
- return $.when.apply($, defs).then(function () {
+ return Promise.all(defs).then(function (groups) {
if (!options || !options.onlyGroups) {
// generate the res_ids of the main list, being the concatenation
// of the fetched res_ids in each group
- list.res_ids = _.flatten(_.map(arguments, function (group) {
+ list.res_ids = _.flatten(_.map(groups, function (group) {
return group ? group.res_ids : [];
}));
}
return list;
}).then(function () {
- var fetchingDefs = [];
- fetchingDefs.push(self._fetchX2ManysSingleBatch(list));
- fetchingDefs.push(self._fetchReferencesSingleBatch(list));
- return $.when.apply($, fetchingDefs).then(function () {
+ return Promise.all([
+ self._fetchX2ManysSingleBatch(list),
+ self._fetchReferencesSingleBatch(list)
+ ]).then(function () {
return list;
});
});
@@ -4309,11 +4318,11 @@ var BasicModel = AbstractModel.extend({
* instead of a /search_read.
*
* @param {Object} list a valid resource object
- * @returns {Deferred<Object>} resolves to the fetched list object
+ * @returns {Promise<Object>} resolves to the fetched list object
*/
_readUngroupedList: function (list) {
var self = this;
- var def = $.when();
+ var def = Promise.resolve();
// generate the current count and res_ids list by applying the changes
list = this._applyX2ManyOperations(list);
@@ -4375,7 +4384,7 @@ var BasicModel = AbstractModel.extend({
* @param {Object} [options]
* @param {boolean} [options.keepChanges=false] if true, doesn't discard the
* changes on the record before reloading it
- * @returns {Deferred<string>} resolves to the id of the resource
+ * @returns {Promise<string>} resolves to the id of the resource
*/
_reload: function (id, options) {
options = options || {};
@@ -4464,7 +4473,7 @@ var BasicModel = AbstractModel.extend({
* /search_read because the data may not be static (for ex, a list view).
*
* @param {Object} list
- * @returns {Deferred}
+ * @returns {Promise}
*/
_searchReadUngroupedList: function (list) {
var self = this;