Use synchronous calls for on_change's and Field.set

Those calls can be triggered by event like focus-out. So to ensure a correct
order of the flow, we must defer the processing of the result.

issue8482
review273571002
This commit is contained in:
Cédric Krier committed 2019-07-17 22:40:48 +02:00
1 parent 65b553c660
commit 12b60612ea
4 files changed
+176 -224

No files matched your search

+160 -201
View File
@@ -115,10 +115,9 @@
record._changed.id = true;
});
var root_group = this.root_group;
this.changed().then(function() {
root_group.screens.forEach(function(screen) {
screen.display();
});
this.changed();
root_group.screens.forEach(function(screen) {
screen.display();
});
}
};
@@ -233,17 +232,9 @@
}));
}
this.parent._changed[this.child_name] = true;
var changed_prm = this.parent.model.fields[this.child_name]
.changed(this.parent);
// One2Many.changed could return undefined
if (!changed_prm) {
changed_prm = jQuery.when();
}
return changed_prm.then(function() {
this.parent.validate(null, true).done(function() {
return this.parent.group.changed();
}.bind(this));
}.bind(this));
this.parent.model.fields[this.child_name].changed(this.parent);
this.parent.validate(null, true, false, true);
this.parent.group.changed();
};
array.delete_ = function(records) {
if (jQuery.isEmptyObject(records)) {
@@ -742,7 +733,6 @@
validate = true;
}
var name, value;
var promises = [];
var rec_named_fields = ['many2one', 'one2one', 'reference'];
var later = {};
var fieldnames = [];
@@ -772,22 +762,18 @@
var related = name + '.';
this._values[related] = values[related] || {};
}
promises.push(this.model.fields[name].set(this, value));
this.model.fields[name].set(this, value);
this._loaded[name] = true;
fieldnames.push(name);
}
for (name in later) {
value = later[name];
promises.push(this.model.fields[name].set(this, value));
this.model.fields[name].set(this, value);
this._loaded[name] = true;
}
if (validate) {
this.validate(fieldnames, true, false, false);
}
return jQuery.when.apply(jQuery, promises.filter(Boolean))
.then(function() {
for (var i = 0; i < fieldnames.length; i++) {
this._loaded[fieldnames[i]] = true;
}
if (validate) {
return this.validate(fieldnames, true);
}
}.bind(this));
},
get: function() {
var value = {};
@@ -897,24 +883,22 @@
fieldnames.push(fname);
}
return jQuery.when.apply(jQuery, promises).then(function() {
return this.on_change(fieldnames).then(function() {
return this.on_change_with(fieldnames).then(function() {
var callback = function() {
if (display) {
return this.group.root_group.screens
.forEach(function(screen) {
return screen.display();
});
}
}.bind(this);
if (validate) {
return this.validate(null, true)
.then(callback);
} else {
return callback();
}
}.bind(this));
}.bind(this));
this.on_change(fieldnames);
this.on_change_with(fieldnames);
var callback = function() {
if (display) {
return this.group.root_group.screens
.forEach(function(screen) {
return screen.display();
});
}
}.bind(this);
if (validate) {
return this.validate(null, true)
.then(callback);
} else {
return callback();
}
}.bind(this));
},
get_timestamp: function() {
@@ -982,23 +966,22 @@
}
}.bind(this));
if (!jQuery.isEmptyObject(values)) {
var prm;
if (fieldnames.length == 1) {
prm = this.model.execute(
'on_change_' + fieldnames[0],
[values], this.get_context())
.then(this.set_on_change.bind(this));
} else {
prm = this.model.execute('on_change',
[values, fieldnames], this.get_context())
.then(function(changes) {
return jQuery.when.apply(jQuery,
changes.map(this.set_on_change.bind(this)));
}.bind(this));
var changes;
try {
if (fieldnames.length == 1) {
changes = [];
changes.push(this.model.execute(
'on_change_' + fieldnames[0],
[values], this.get_context(), false));
} else {
changes = this.model.execute(
'on_change',
[values, fieldnames], this.get_context(), false);
}
} catch (e) {
return;
}
return prm;
} else {
return jQuery.when();
changes.forEach(this.set_on_change, this);
}
},
on_change_with: function(field_names) {
@@ -1039,48 +1022,43 @@
delete this._values[fieldname + '.'];
}
}
var prms = [];
var prm;
var set_on_change = function(fieldname) {
return function(result) {
return this.model.fields[fieldname].set_on_change(
this, result);
};
};
var result;
fieldnames = Object.keys(fieldnames);
if (fieldnames) {
if (fieldnames.length == 1) {
prm = this.model.execute(
'on_change_with_' + fieldnames[0],
[values], this.get_context());
prms.push(prm.then(set_on_change(fieldnames[0]).bind(this)));
} else {
prm = this.model.execute(
'on_change_with',
[values, fieldnames], this.get_context());
prms.push(prm.then(this.set_on_change.bind(this)));
try {
if (fieldnames.length == 1) {
fieldname = fieldnames[0];
result = {};
result[fieldname] = this.model.execute(
'on_change_with_' + fieldname,
[values], this.get_context(), false);
} else {
result = this.model.execute(
'on_change_with',
[values, fieldnames], this.get_context(), false);
}
} catch (e) {
return;
}
this.set_on_change(result);
}
for (fieldname in later) {
if (!later.hasOwnProperty(fieldname)) {
continue;
}
on_change_with = this.model.fields[fieldname]
.description.on_change_with;
values = this._get_on_change_args(on_change_with);
prm = this.model.execute('on_change_with_' + fieldname,
[values], this.get_context());
prms.push(prm.then(set_on_change(fieldname).bind(this)));
try {
result = this.model.execute(
'on_change_with_' + fieldname,
[values], this.get_context(), false);
} catch (e) {
return;
}
this.model.fields[fieldname].set_on_change(this, result);
}
return jQuery.when.apply(jQuery, prms);
},
set_on_change: function(values) {
var fieldname, value;
var promises = [];
for (fieldname in values) {
if (!values.hasOwnProperty(fieldname)) {
continue;
}
value = values[fieldname];
if (!(fieldname in this.model.fields)) {
continue;
@@ -1092,46 +1070,41 @@
var related = fieldname + '.';
this._values[related] = values[related] || {};
}
promises.push(
this.model.fields[fieldname].set_on_change(this, value));
this.model.fields[fieldname].set_on_change(this, value);
}
return jQuery.when.apply(jQuery, promises);
},
autocomplete_with: function(fieldname) {
var promises = [];
for (var fname in this.model.fields) {
var field = this.model.fields[fname];
var autocomplete = field.description.autocomplete || [];
if (!~autocomplete.indexOf(fieldname)) {
continue;
}
promises.push(this.do_autocomplete(fname));
this.do_autocomplete(fname);
}
return jQuery.when.apply(jQuery, promises);
},
do_autocomplete: function(fieldname) {
this.autocompletion[fieldname] = [];
var field = this.model.fields[fieldname];
var autocomplete = field.description.autocomplete;
var values = this._get_on_change_args(autocomplete);
var prm = this.model.execute(
'autocomplete_' + fieldname, [values], this.get_context());
return prm.then(function(result) {
this.autocompletion[fieldname] = result;
}.bind(this));
var result;
try {
result = this.model.execute(
'autocomplete_' + fieldname, [values], this.get_context(),
false);
} catch (e) {
result = [];
}
this.autocompletion[fieldname] = result;
},
reset: function(value) {
this.cancel();
return this.set(value, true).then(function() {
var promises = [];
if (this.group.parent) {
promises.push(this.group.parent.on_change(
[this.group.child_name]));
promises.push(this.group.parent.on_change_with(
[this.group.child_name]));
}
return jQuery.when.apply(jQuery, promises);
}.bind(this));
this.set(value, true);
if (this.group.parent) {
this.group.parent.on_change([this.group.child_name]);
this.group.parent.on_change_with([this.group.child_name]);
}
},
expr_eval: function(expr) {
if (typeof(expr) != 'string') return expr;
@@ -1396,20 +1369,16 @@
if (JSON.stringify(previous_value) !=
JSON.stringify(this.get(record))) {
record._changed[this.name] = true;
this.changed(record).done(function() {
record.validate(null, true).then(function() {
record.group.changed();
});
});
this.changed(record);
record.validate(null, true, false, true);
record.group.changed();
} else if (force_change) {
record._changed[this.name] = true;
this.changed(record).done(function() {
record.validate(null, true).then(function() {
var root_group = record.group.root_group;
root_group.screens.forEach(function(screen) {
screen.display();
});
});
this.changed(record);
record.validate(null, true, false, true);
var root_group = record.group.root_group;
root_group.screens.forEach(function(screen) {
screen.display();
});
}
},
@@ -1425,13 +1394,10 @@
record._changed[this.name] = true;
},
changed: function(record) {
return record.on_change([this.name]).then(function() {
return record.on_change_with([this.name]).then(function() {
return record.autocomplete_with(this.name).then(function() {
record.set_field_context();
});
}.bind(this));
}.bind(this));
record.on_change([this.name]);
record.on_change_with([this.name]);
record.autocomplete_with(this.name);
record.set_field_context();
},
get_timestamp: function(record) {
return {};
@@ -1959,39 +1925,35 @@
'.fields_get',
'params': [Object.keys(field_names), context]
};
prm = Sao.rpc(args, record.model.session);
}
}
var set_value = function(fields) {
var promises = [];
if (!jQuery.isEmptyObject(fields)) {
try {
fields = Sao.rpc(args, record.model.session, false);
} catch (e) {
return;
}
group.add_fields(fields);
}
if (mode == 'list ids') {
for (var i = 0, len = group.length; i < len; i++) {
var old_record = group[i];
if (!~value.indexOf(old_record.id)) {
group.remove(old_record, true);
}
}
if (mode == 'list ids') {
for (var i = 0, len = group.length; i < len; i++) {
var old_record = group[i];
if (!~value.indexOf(old_record.id)) {
group.remove(old_record, true);
}
group.load(value, modified);
} else {
value.forEach(function(vals) {
var new_record = group.new_(false);
if (default_) {
// Don't validate as parent will validate
promises.push(new_record.set_default(
vals, false, false));
group.add(new_record, -1, false);
} else {
promises.push(new_record.set(vals));
group.push(new_record);
}
});
}
return jQuery.when.apply(jQuery, promises);
};
return prm.then(set_value.bind(this));
group.load(value, modified);
} else {
value.forEach(function(vals) {
var new_record = group.new_(false);
if (default_) {
// Don't validate as parent will validate
new_record.set_default(vals, false, false);
group.add(new_record, -1, false);
} else {
new_record.set(vals);
group.push(new_record);
}
});
}
},
set: function(record, value, _default) {
if (_default === undefined) {
@@ -2009,7 +1971,7 @@
}
record._values[this.name] = undefined;
this._set_default_value(record, model);
return this._set_value(record, value, _default);
this._set_value(record, value, _default);
},
get: function(record) {
var group = record._values[this.name];
@@ -2083,20 +2045,16 @@
this._set_value(record, value, false, modified);
if (modified) {
record._changed[this.name] = true;
this.changed(record).done(function() {
record.validate(null, true).then(function() {
record.group.changed();
});
});
this.changed(record);
record.validate(null, true, false, true);
record.group.changed();
} else if (force_change) {
record._changed[this.name] = true;
this.changed(record).done(function() {
record.validate(null, true).then(function() {
var root_group = record.group.root_group;
root_group.screens.forEach(function(screen) {
screen.display();
});
});
this.changed(record);
record.validate(null, true, false, true);
var root_group = record.group.root_group;
root_group.screens.forEach(function(screen) {
screen.display();
});
}
},
@@ -2109,15 +2067,15 @@
return this.set(record, value, true);
},
set_on_change: function(record, value) {
var fields;
record._changed[this.name] = true;
this._set_default_value(record);
if (value instanceof Array) {
return this._set_value(record, value, false, true);
}
var prm = jQuery.when();
if (value.add || value.update) {
var context = this.get_context(record);
var fields = record._values[this.name].model.fields;
fields = record._values[this.name].model.fields;
var field_names = {};
var adding_values = [];
if (value.add) {
@@ -2144,7 +2102,13 @@
'.fields_get',
'params': [Object.keys(field_names), context]
};
prm = Sao.rpc(args, record.model.session);
try {
fields = Sao.rpc(args, record.model.session, false);
} catch (e) {
return;
}
} else {
fields = {};
}
}
@@ -2168,33 +2132,28 @@
}.bind(this));
if (value.add || value.update) {
prm = prm.then(function(fields) {
var promises = [];
group.add_fields(fields);
if (value.add) {
value.add.forEach(function(vals) {
var index = vals[0];
var data = vals[1];
var new_record = group.new_(false);
group.add(new_record, index, false);
promises.push(new_record.set_on_change(data));
});
}
if (value.update) {
value.update.forEach(function(vals) {
if (!vals.id) {
return;
}
var record2 = group.get(vals.id);
if (record2) {
promises.push(record2.set_on_change(vals));
}
});
}
return jQuery.when.apply(jQuery, promises);
}.bind(this));
group.add_fields(fields);
if (value.add) {
value.add.forEach(function(vals) {
var index = vals[0];
var data = vals[1];
var new_record = group.new_(false);
group.add(new_record, index, false);
new_record.set_on_change(data);
});
}
if (value.update) {
value.update.forEach(function(vals) {
if (!vals.id) {
return;
}
var record2 = group.get(vals.id);
if (record2) {
record2.set_on_change(vals);
}
});
}
}
return prm;
},
_set_default_value: function(record, model) {
if (record._values[this.name] !== undefined) {
+3 -7
View File
@@ -1365,15 +1365,11 @@
return prm.then(function() {
group.add(record, this.new_model_position());
this.current_record = record;
var prm = jQuery.when();
if (previous_view.view_type == 'calendar') {
prm = previous_view.set_default_date(
record, selected_date);
previous_view.set_default_date(record, selected_date);
}
prm.then(function() {
this.display().done(function() {
this.set_cursor(true, true);
}.bind(this));
this.display().done(function() {
this.set_cursor(true, true);
}.bind(this));
return record;
}.bind(this));
+2 -3
View File
@@ -306,9 +306,8 @@
selected_date = Sao.Date(selected_date);
}
field.set(record, selected_date);
return record.on_change([dtstart]).then(function() {
return record.on_change_with([dtstart]);
});
record.on_change([dtstart]);
record.on_change_with([dtstart]);
},
get_selected_date: function(){
return this.el.fullCalendar('getDate');
+11 -13
View File
@@ -1301,22 +1301,20 @@ function eval_pyson(value){
var record = this.record;
if (this.datalist) {
this.datalist.empty();
var set_autocompletion = function() {
var selection = [];
if (record) {
selection = record.autocompletion[this.field_name] || [];
var selection;
if (record) {
if (!(this.field_name in record.autocompletion)) {
record.do_autocomplete(this.field_name);
}
selection.forEach(function(e) {
jQuery('<option/>', {
'value': e
}).appendTo(this.datalist);
}.bind(this));
}.bind(this);
if (record && !(this.field_name in record.autocompletion)) {
record.do_autocomplete(this.field_name).done(set_autocompletion);
selection = record.autocompletion[this.field_name] || [];
} else {
set_autocompletion();
selection = [];
}
selection.forEach(function(e) {
jQuery('<option/>', {
'value': e
}).appendTo(this.datalist);
}.bind(this));
}
// Set size