summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/fields
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/fields
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/fields')
-rw-r--r--addons/web/static/src/js/fields/abstract_field.js49
-rw-r--r--addons/web/static/src/js/fields/basic_fields.js23
-rw-r--r--addons/web/static/src/js/fields/field_registry.js2
-rw-r--r--addons/web/static/src/js/fields/relational_fields.js257
-rw-r--r--addons/web/static/src/js/fields/upgrade_fields.js2
5 files changed, 175 insertions, 158 deletions
diff --git a/addons/web/static/src/js/fields/abstract_field.js b/addons/web/static/src/js/fields/abstract_field.js
index edc7c03ac02..822c90c31f4 100644
--- a/addons/web/static/src/js/fields/abstract_field.js
+++ b/addons/web/static/src/js/fields/abstract_field.js
@@ -184,7 +184,7 @@ var AbstractField = Widget.extend({
* When a field widget is appended to the DOM, its start method is called,
* and will automatically call render. Most widgets should not override this.
*
- * @returns {Deferred}
+ * @returns {Promise}
*/
start: function () {
var self = this;
@@ -236,7 +236,7 @@ var AbstractField = Widget.extend({
* if the value changed but was not notified.
*
* @abstract
- * @returns {Deferred|undefined}
+ * @returns {Promise|undefined}
*/
commitChanges: function () {},
/**
@@ -290,12 +290,12 @@ var AbstractField = Widget.extend({
* is optional, and may be used by a widget to share information from the
* moment a field change event is triggered to the moment a reset
* operation is applied.
- * @returns {Deferred} A Deferred, which resolves when the widget rendering
+ * @returns {Promise} A promise, which resolves when the widget rendering
* is complete
*/
reset: function (record, event) {
this._reset(record, event);
- return this._render() || $.when();
+ return this._render() || Promise.resolve();
},
/**
* Remove the invalid class on a field
@@ -396,7 +396,7 @@ var AbstractField = Widget.extend({
* synchronous.
*
* @private
- * @returns {Deferred|undefined}
+ * @returns {Promise|undefined}
*/
_render: function () {
if (this.attrs.decorations) {
@@ -413,7 +413,7 @@ var AbstractField = Widget.extend({
* concrete widget.
*
* @private
- * @returns {Deferred|undefined}
+ * @returns {Promise|undefined}
*/
_renderEdit: function () {
},
@@ -422,7 +422,7 @@ var AbstractField = Widget.extend({
* the concrete widget.
*
* @private
- * @returns {Deferred|undefined}
+ * @returns {Promise|undefined}
*/
_renderReadonly: function () {
},
@@ -457,13 +457,13 @@ var AbstractField = Widget.extend({
* will not notify and not trigger the onchange, even though it was changed.
* @param {boolean} [options.forceChange=false] if true, the change event will be
* triggered even if the new value is the same as the old one
- * @returns {Deferred}
+ * @returns {Promise}
*/
_setValue: function (value, options) {
// we try to avoid doing useless work, if the value given has not
// changed. Note that we compare the unparsed values.
if (this.lastSetValue === value || (this.value === false && value === '')) {
- return $.when();
+ return Promise.resolve();
}
this.lastSetValue = value;
try {
@@ -472,25 +472,26 @@ var AbstractField = Widget.extend({
} catch (e) {
this._isValid = false;
this.trigger_up('set_dirty', {dataPointID: this.dataPointID});
- return $.Deferred().reject();
+ return Promise.reject({message: "Value set is not valid"});
}
if (!(options && options.forceChange) && this._isSameValue(value)) {
- return $.when();
+ return Promise.resolve();
}
- var def = $.Deferred();
- var changes = {};
- changes[this.name] = value;
- this.trigger_up('field_changed', {
- dataPointID: this.dataPointID,
- changes: changes,
- viewType: this.viewType,
- doNotSetDirty: options && options.doNotSetDirty,
- notifyChange: !options || options.notifyChange !== false,
- allowWarning: options && options.allowWarning,
- onSuccess: def.resolve.bind(def),
- onFailure: def.reject.bind(def),
+ var self = this;
+ return new Promise(function (resolve, reject) {
+ var changes = {};
+ changes[self.name] = value;
+ self.trigger_up('field_changed', {
+ dataPointID: self.dataPointID,
+ changes: changes,
+ viewType: self.viewType,
+ doNotSetDirty: options && options.doNotSetDirty,
+ notifyChange: !options || options.notifyChange !== false,
+ allowWarning: options && options.allowWarning,
+ onSuccess: resolve,
+ onFailure: reject,
+ });
});
- return def;
},
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/fields/basic_fields.js b/addons/web/static/src/js/fields/basic_fields.js
index c6df3885a86..a87ff3e4436 100644
--- a/addons/web/static/src/js/fields/basic_fields.js
+++ b/addons/web/static/src/js/fields/basic_fields.js
@@ -213,7 +213,7 @@ var InputField = DebouncedField.extend({
// sure they are recomputed.
this._applyDecorations();
}
- return $.when();
+ return Promise.resolve();
} else {
return this._render();
}
@@ -486,8 +486,6 @@ var LinkButton = AbstractField.extend({
});
-
-
var FieldDate = InputField.extend({
className: "o_field_date",
tagName: "span",
@@ -513,7 +511,7 @@ var FieldDate = InputField.extend({
*/
start: function () {
var self = this;
- var def;
+ var prom;
if (this.mode === 'edit') {
this.datewidget = this._makeDatePicker();
this.datewidget.on('datetime_changed', this, function () {
@@ -522,13 +520,13 @@ var FieldDate = InputField.extend({
this._setValue(value);
}
});
- def = this.datewidget.appendTo('<div>').done(function () {
+ prom = this.datewidget.appendTo('<div>').then(function () {
self.datewidget.$el.addClass(self.$el.attr('class'));
self._prepareInput(self.datewidget.$input);
self._replaceElement(self.datewidget.$el);
});
}
- return $.when(def, this._super.apply(this, arguments));
+ return Promise.resolve(prom).then(this._super.bind(this));
},
//--------------------------------------------------------------------------
@@ -728,7 +726,7 @@ var FieldMonetary = InputField.extend({
this.$el.empty();
// Prepare and add the input
- this._prepareInput(this.$input).appendTo(this.$el);
+ var def = this._prepareInput(this.$input).appendTo(this.$el);
if (this.currency) {
// Prepare and add the currency symbol
@@ -739,6 +737,7 @@ var FieldMonetary = InputField.extend({
this.$el.prepend($currencySymbol);
}
}
+ return def;
},
/**
* @override
@@ -1136,7 +1135,7 @@ var FieldText = InputField.extend(TranslatableFieldMixin, {
*/
reset: function () {
var self = this;
- return $.when(this._super.apply(this, arguments)).then(function () {
+ return Promise.resolve(this._super.apply(this, arguments)).then(function () {
if (self.mode === 'edit') {
self.$input.trigger('change');
}
@@ -2568,7 +2567,7 @@ var JournalDashboardGraph = AbstractField.extend({
if (this._isInDOM) {
return this._renderInDOM();
}
- return $.when();
+ return Promise.resolve();
},
/**
* Render the widget. This function assumes that it is attached to the DOM.
@@ -2743,13 +2742,13 @@ var FieldDomain = AbstractField.extend({
/**
* @private
* @override _render from AbstractField
- * @returns {Deferred}
+ * @returns {Promise}
*/
_render: function () {
// If there is no model, only change the non-domain-selector content
if (!this._domainModel) {
this._replaceContent();
- return $.when();
+ return Promise.resolve();
}
// Convert char value to array value
@@ -2901,7 +2900,7 @@ var AceEditor = DebouncedField.extend({
/**
* @override start from AbstractField (Widget)
*
- * @returns {Deferred}
+ * @returns {Promise}
*/
start: function () {
this._startAce(this.$('.ace-view-editor')[0]);
diff --git a/addons/web/static/src/js/fields/field_registry.js b/addons/web/static/src/js/fields/field_registry.js
index 35d10c5a5e4..0e0ba3dc117 100644
--- a/addons/web/static/src/js/fields/field_registry.js
+++ b/addons/web/static/src/js/fields/field_registry.js
@@ -6,7 +6,7 @@ var Registry = require('web.Registry');
return new Registry();
});
-odoo.define('web._field_registry', function(require) {
+odoo.define('web._field_registry', function (require) {
"use strict";
var AbstractField = require('web.AbstractField');
diff --git a/addons/web/static/src/js/fields/relational_fields.js b/addons/web/static/src/js/fields/relational_fields.js
index bd46faf1e08..469cedb8ff1 100644
--- a/addons/web/static/src/js/fields/relational_fields.js
+++ b/addons/web/static/src/js/fields/relational_fields.js
@@ -171,11 +171,11 @@ var FieldMany2One = AbstractField.extend({
* is saved.
*
* @override
- * @returns {Deferred} resolved as soon as there is no longer record being
+ * @returns {Promise} resolved as soon as there is no longer record being
* (quick) created
*/
commitChanges: function () {
- return $.when(this.createDef);
+ return Promise.resolve(this.createDef);
},
/**
* @override
@@ -208,7 +208,7 @@ var FieldMany2One = AbstractField.extend({
this.isDirty = false;
}
if (this.isDirty) {
- return $.when();
+ return Promise.resolve();
} else {
return this._render();
}
@@ -257,9 +257,9 @@ var FieldMany2One = AbstractField.extend({
if (!source.validation || source.validation.call(self, req.term)) {
source.loading = true;
- // Wrap the returned value of the source.method with $.when.
+ // Wrap the returned value of the source.method with a promise
// So event if the returned value is not async, it will work
- $.when(source.method.call(self, req.term)).then(function (results) {
+ Promise.resolve(source.method.call(self, req.term)).then(function (results) {
source.results = results;
source.loading = false;
resp(self._concatenateAutocompleteResults());
@@ -366,49 +366,55 @@ var FieldMany2One = AbstractField.extend({
/**
* @private
* @param {string} name
- * @returns {Deferred} resolved after the name_create or when the slowcreate
+ * @returns {Promise} resolved after the name_create or when the slowcreate
* modal is closed.
*/
_quickCreate: function (name) {
var self = this;
- var def = $.Deferred();
- this.createDef = this.createDef || $.Deferred();
- // called when the record has been quick created, or when the dialog has
- // been closed (in the case of a 'slow' create), meaning that the job is
- // done
- var createDone = function () {
- def.resolve();
- self.createDef.resolve();
- self.createDef = undefined;
- };
- // called if the quick create is disabled on this many2one, or if the
- // quick creation failed (probably because there are mandatory fields on
- // the model)
- var slowCreate = function () {
- var dialog = self._searchCreatePopup("form", false, self._createContext(name));
- dialog.on('closed', self, createDone);
- };
- if (this.nodeOptions.quick_create) {
- var nameCreateDef = this._rpc({
- model: this.field.relation,
- method: 'name_create',
- args: [name],
- context: this.record.getContext(this.recordParams),
- }).fail(function (error, ev) {
- ev.preventDefault();
- slowCreate();
+ var createDone;
+
+ var def = new Promise(function (resolve, reject) {
+ self.createDef = new Promise(function (innerResolve) {
+ // called when the record has been quick created, or when the dialog has
+ // been closed (in the case of a 'slow' create), meaning that the job is
+ // done
+ createDone = function () {
+ innerResolve();
+ resolve();
+ self.createDef = undefined;
+ };
});
- this.dp.add(nameCreateDef)
- .then(function (result) {
- if (self.mode === "edit") {
- self.reinitialize({id: result[0], display_name: result[1]});
- }
- createDone();
- })
- .fail(def.reject.bind(def));
- } else {
- slowCreate();
- }
+
+ // called if the quick create is disabled on this many2one, or if the
+ // quick creation failed (probably because there are mandatory fields on
+ // the model)
+ var slowCreate = function () {
+ var dialog = self._searchCreatePopup("form", false, self._createContext(name));
+ dialog.on('closed', self, createDone);
+ };
+ if (self.nodeOptions.quick_create) {
+ var nameCreateDef = self._rpc({
+ model: self.field.relation,
+ method: 'name_create',
+ args: [name],
+ context: self.record.getContext(self.recordParams),
+ }).guardedCatch(function (reason) {
+ reason.event.preventDefault();
+ slowCreate();
+ });
+ self.dp.add(nameCreateDef)
+ .then(function (result) {
+ if (self.mode === "edit") {
+ self.reinitialize({id: result[0], display_name: result[1]});
+ }
+ createDone();
+ })
+ .guardedCatch(reject);
+ } else {
+ slowCreate();
+ }
+ });
+
return def;
},
/**
@@ -456,35 +462,32 @@ var FieldMany2One = AbstractField.extend({
*
* @private
* @param {string} search_val
- * @returns {Deferred}
+ * @returns {Promise}
*/
_search: function (search_val) {
var self = this;
- var def = $.Deferred();
- this.orderer.add(def);
-
- var context = this.record.getContext(this.recordParams);
- var domain = this.record.getDomain(this.recordParams);
+ var def = new Promise(function (resolve, reject) {
+ var context = self.record.getContext(self.recordParams);
+ var domain = self.record.getDomain(self.recordParams);
- // Add the additionalContext
- _.extend(context, this.additionalContext);
+ // Add the additionalContext
+ _.extend(context, self.additionalContext);
- var blacklisted_ids = this._getSearchBlacklist();
- if (blacklisted_ids.length > 0) {
- domain.push(['id', 'not in', blacklisted_ids]);
- }
+ var blacklisted_ids = self._getSearchBlacklist();
+ if (blacklisted_ids.length > 0) {
+ domain.push(['id', 'not in', blacklisted_ids]);
+ }
- this._rpc({
- model: this.field.relation,
- method: "name_search",
- kwargs: {
- name: search_val,
- args: domain,
- operator: "ilike",
- limit: this.limit + 1,
- context: context,
- }})
- .then(function (result) {
+ self._rpc({
+ model: self.field.relation,
+ method: "name_search",
+ kwargs: {
+ name: search_val,
+ args: domain,
+ operator: "ilike",
+ limit: self.limit + 1,
+ context: context,
+ }}).then(function (result) {
// possible selections for the m2o
var values = _.map(result, function (x) {
x[1] = self._getDisplayName(x[1]);
@@ -516,7 +519,7 @@ var FieldMany2One = AbstractField.extend({
},
});
}
- $.when(prom).then(function (results) {
+ Promise.resolve(prom).then(function (results) {
var dynamicFilters;
if (results) {
var ids = _.map(results, function (x) {
@@ -563,9 +566,10 @@ var FieldMany2One = AbstractField.extend({
});
}
- def.resolve(values);
+ resolve(values);
});
-
+ });
+ this.orderer.add(def);
return def;
},
/**
@@ -670,8 +674,9 @@ var FieldMany2One = AbstractField.extend({
readonly: !self.can_write,
on_saved: function (record, changed) {
if (changed) {
- self._setValue(self.value.data, {forceChange: true});
- self.trigger_up('reload', {db_id: self.value.id});
+ self._setValue(self.value.data, {forceChange: true}).then(function() {
+ self.trigger_up('reload', {db_id: self.value.id});
+ });
}
},
}).open();
@@ -868,14 +873,14 @@ var FieldX2Many = AbstractField.extend({
* user if he wants to discard it if necessary.
*
* @override
- * @returns {Deferred}
+ * @returns {Promise}
*/
commitChanges: function () {
var self = this;
var inEditionRecordID =
- this.renderer
- && this.renderer.viewType === "list"
- && this.renderer.getEditableRecordID();
+ this.renderer &&
+ this.renderer.viewType === "list" &&
+ this.renderer.getEditableRecordID();
if (inEditionRecordID) {
return this.renderer.commitChanges(inEditionRecordID).then(function () {
return self._saveLine(inEditionRecordID);
@@ -894,7 +899,7 @@ var FieldX2Many = AbstractField.extend({
* @param {Object} record
* @param {OdooEvent} [ev] an event that triggered the reset action
* @param {Boolean} [fieldChanged] if true, the widget field has changed
- * @returns {Deferred}
+ * @returns {Promise}
*/
reset: function (record, ev, fieldChanged) {
// If 'fieldChanged' is false, it means that the reset was triggered by
@@ -904,7 +909,7 @@ var FieldX2Many = AbstractField.extend({
if (!fieldChanged) {
var newEval = this._evalColumnInvisibleFields();
if (_.isEqual(this.currentColInvisibleFields, newEval)) {
- return $.when();
+ return Promise.resolve();
}
} else if (ev && ev.target === this && ev.data.changes && this.view.arch.tag === 'tree') {
var command = ev.data.changes[this.name];
@@ -994,17 +999,20 @@ var FieldX2Many = AbstractField.extend({
*
* @override
* @private
- * @returns {Deferred|undefined}
+ * @returns {Promise|undefined}
*/
_render: function () {
+ var self = this;
if (!this.view) {
return this._super();
}
if (this.renderer) {
this.currentColInvisibleFields = this._evalColumnInvisibleFields();
- this.renderer.updateState(this.value, {'columnInvisibleFields': this.currentColInvisibleFields});
- this.pager.updateState({ size: this.value.count });
- return $.when();
+ return this.renderer.updateState(this.value, {
+ columnInvisibleFields: this.currentColInvisibleFields,
+ }).then(function () {
+ self.pager.updateState({ size: self.value.count });
+ });
}
var arch = this.view.arch;
var viewType;
@@ -1050,11 +1058,11 @@ var FieldX2Many = AbstractField.extend({
* Prepends the control panel's $el to this widget's $el.
*
* @private
- * @returns {Deferred}
+ * @returns {Promise}
*/
_renderControlPanel: function () {
if (!this.view) {
- return $.when();
+ return Promise.resolve();
}
var self = this;
var defs = [];
@@ -1072,7 +1080,7 @@ var FieldX2Many = AbstractField.extend({
validate: function () {
var isList = self.view.arch.tag === 'tree';
// TODO: we should have some common method in the basic renderer...
- return isList ? self.renderer.unselectRow() : $.when();
+ return isList ? self.renderer.unselectRow() : Promise.resolve();
},
});
this.pager.on('pager_changed', this, function (new_state) {
@@ -1089,7 +1097,7 @@ var FieldX2Many = AbstractField.extend({
this._renderButtons();
defs.push(this.pager.appendTo($('<div>'))); // start the pager
defs.push(cpDef);
- return $.when.apply($, defs).then(function () {
+ return Promise.all(defs).then(function () {
self._controlPanel.updateContents({
cp_content: {
$buttons: self.$buttons,
@@ -1123,24 +1131,24 @@ var FieldX2Many = AbstractField.extend({
*
* @private
* @param {string} recordID
- * @returns {Deferred} resolved if the line was properly saved or discarded.
+ * @returns {Promise} resolved if the line was properly saved or discarded.
* rejected if the line could not be saved and the user
* did not agree to discard.
*/
_saveLine: function (recordID) {
var self = this;
- var def = $.Deferred();
- var fieldNames = this.renderer.canBeSaved(recordID);
- if (fieldNames.length) {
- this.trigger_up('discard_changes', {
- recordID: recordID,
- onSuccess: def.resolve.bind(def),
- onFailure: def.reject.bind(def),
- });
- } else {
- this.renderer.setRowMode(recordID, 'readonly').done(def.resolve.bind(def));
- }
- return def.then(function () {
+ return new Promise(function (resolve, reject) {
+ var fieldNames = self.renderer.canBeSaved(recordID);
+ if (fieldNames.length) {
+ self.trigger_up('discard_changes', {
+ recordID: recordID,
+ onSuccess: resolve,
+ onFailure: reject,
+ });
+ } else {
+ self.renderer.setRowMode(recordID, 'readonly').then(resolve);
+ }
+ }).then(function () {
self.pager.updateState({ size: self.value.count });
var newEval = self._evalColumnInvisibleFields();
if (!_.isEqual(self.currentColInvisibleFields, newEval)) {
@@ -1233,7 +1241,7 @@ var FieldX2Many = AbstractField.extend({
this.trigger_up('edited_list', { id: this.value.id });
var editedRecord = this.value.data[ev.data.index];
this.renderer.setRowMode(editedRecord.id, 'edit')
- .done(ev.data.onSuccess);
+ .then(ev.data.onSuccess);
},
/**
* Updates the given record with the changes.
@@ -1260,11 +1268,11 @@ var FieldX2Many = AbstractField.extend({
operation: 'UPDATE',
id: ev.data.dataPointID,
data: changes,
- }).done(function () {
+ }).then(function () {
if (ev.data.onSuccess) {
ev.data.onSuccess();
}
- }).fail(function () {
+ }).guardedCatch(function () {
if (ev.data.onFailure) {
ev.data.onFailure();
}
@@ -1332,8 +1340,8 @@ var FieldX2Many = AbstractField.extend({
self.trigger_up('mutexify', {
action: function () {
return self._saveLine(ev.data.recordID)
- .done(ev.data.onSuccess)
- .fail(ev.data.onFailure);
+ .then(ev.data.onSuccess)
+ .guardedCatch(ev.data.onFailure);
},
});
});
@@ -1361,18 +1369,20 @@ var FieldX2Many = AbstractField.extend({
notifyChange: false,
});
});
- $.when.apply($, defs).then(function () {
+ Promise.all(defs).then(function () {
+ function always() {
+ self.trigger_up('toggle_column_order', {
+ id: self.value.id,
+ name: event.data.handleField,
+ });
+ }
+
// trigger only once the onchange for parent record
self._setValue({
operation: 'UPDATE',
id: rowID,
data: _.object([event.data.handleField], [event.data.offset + rowIDs.length]),
- }).always(function () {
- self.trigger_up('toggle_column_order', {
- id: self.value.id,
- name: event.data.handleField,
- });
- });
+ }).then(always).guardedCatch(always);
});
},
/**
@@ -1418,7 +1428,7 @@ var FieldOne2Many = FieldX2Many.extend({
* @override
* @param {Object} record
* @param {OdooEvent} [ev] an event that triggered the reset action
- * @returns {Deferred}
+ * @returns {Promise}
*/
reset: function (record, ev) {
var self = this;
@@ -1527,13 +1537,16 @@ var FieldOne2Many = FieldX2Many.extend({
context: data.context,
}, {
allowWarning: data.allowWarning
- }).always(function () {
+ }).then(function () {
self.creatingRecord = false;
- }).done(function (){
+ }).then(function (){
if (data.onSuccess){
data.onSuccess();
}
- });
+ }).guardedCatch(function() {
+ self.creatingRecord = false;
+ })
+ ;
}
} else {
this._openFormDialog({
@@ -1646,8 +1659,10 @@ var FieldMany2Many = FieldX2Many.extend({
domain: this.record.getDomain(this.recordParams),
fields_view: this.attrs.views && this.attrs.views.form,
on_saved: function () {
- self._setValue({operation: 'TRIGGER_ONCHANGE'}, {forceChange: true});
- self.trigger_up('reload', {db_id: ev.data.id});
+ self._setValue({operation: 'TRIGGER_ONCHANGE'}, {forceChange: true})
+ .then(function () {
+ self.trigger_up('reload', {db_id: ev.data.id});
+ });
},
on_remove: function () {
self._setValue({operation: 'FORGET', ids: [ev.data.id]});
@@ -1918,10 +1933,12 @@ var FieldMany2ManyTags = AbstractField.extend({
* @override
*/
reset: function (record, event) {
- this._super.apply(this, arguments);
- if (event && event.target === this) {
- this.activate();
- }
+ var self = this;
+ return this._super.apply(this, arguments).then(function(){
+ if (event && event.target === self) {
+ self.activate();
+ }
+ });
},
//--------------------------------------------------------------------------
diff --git a/addons/web/static/src/js/fields/upgrade_fields.js b/addons/web/static/src/js/fields/upgrade_fields.js
index dd91ca2efc2..8dd1f9d3ae8 100644
--- a/addons/web/static/src/js/fields/upgrade_fields.js
+++ b/addons/web/static/src/js/fields/upgrade_fields.js
@@ -34,7 +34,7 @@ var AbstractFieldUpgrade = {
* Redirects the user to the odoo-enterprise/uprade page
*
* @private
- * @returns {Deferred}
+ * @returns {Promise}
*/
_confirmUpgrade: function () {
return this._rpc({