[FIX] web: Listview editable
(backport of enterprise version) Fix various problems with list view editable : * Click outside which should discard/save the line in edition * Warnings which should not appear when line is being discarded/saved * JS test + JS code refactoring
This commit is contained in:
@@ -14,7 +14,6 @@ var utils = require('web.utils');
|
||||
var ViewManager = require('web.ViewManager');
|
||||
var formats = require('web.formats');
|
||||
|
||||
|
||||
var _t = core._t;
|
||||
var QWeb = core.qweb;
|
||||
var COMMANDS = common.commands;
|
||||
@@ -854,7 +853,7 @@ var FieldX2Many = AbstractManyField.extend({
|
||||
var view = this.get_active_view();
|
||||
if (view && view.type === "list" && view.controller.__focus) {
|
||||
var def = $.Deferred();
|
||||
view.controller._on_blur_one2many(true).always(function () {
|
||||
view.controller._on_blur_one2many().always(function () {
|
||||
def.resolve();
|
||||
});
|
||||
return def;
|
||||
@@ -904,7 +903,6 @@ var X2ManyViewManager = ViewManager.extend({
|
||||
this.set_cp_bus(this.control_panel.get_bus());
|
||||
this._super(parent, dataset, views, flags);
|
||||
this.registry = core.view_registry.extend(x2many_views);
|
||||
this.__ignore_blur = false;
|
||||
},
|
||||
start: function() {
|
||||
this.control_panel.prependTo(this.$el);
|
||||
@@ -1004,12 +1002,6 @@ var X2ManyList = ListView.List.extend({
|
||||
'class': 'oe_form_field_x2many_list_row_add'
|
||||
}).append(
|
||||
$('<a>', {href: '#'}).text(_t("Add an item"))
|
||||
.mousedown(function () {
|
||||
// FIXME: needs to be an official API somehow
|
||||
if (self.view.editor.is_editing()) {
|
||||
self.view.__ignore_blur = true;
|
||||
}
|
||||
})
|
||||
.click(function (e) {
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
@@ -1018,7 +1010,7 @@ var X2ManyList = ListView.List.extend({
|
||||
clearTimeout(self.view.editor.form.__blur_timeout);
|
||||
self.view.editor.form.__blur_timeout = false;
|
||||
}
|
||||
self.view.ensure_saved().done(function () {
|
||||
self.view.save_edition().done(function () {
|
||||
self.view.do_add_record();
|
||||
});
|
||||
}));
|
||||
@@ -1045,23 +1037,17 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
this.on('save:before cancel:before', this, this.proxy('_before_unedit'));
|
||||
|
||||
/* detect if the user try to exit the one2many widget */
|
||||
var self = this;
|
||||
this._mousedown_blur_line = function (event) {
|
||||
if (self.__focus) {
|
||||
self.__ignore_blur = true;
|
||||
if ($(event.target).closest("button, *:not(.oe_form_field_x2many_list_row_add) > a").length ||
|
||||
(!$(event.target).closest(self.editor.$el[0]).length && !$(event.target).closest(self.$el[0]).length)) {
|
||||
self.__ignore_blur = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
$(document).on('mousedown', this._mousedown_blur_line);
|
||||
core.bus.on('click', this, this._on_click_outside);
|
||||
|
||||
this.dataset.on('dataset_changed', this, function () {
|
||||
self._dataset_changed = true;
|
||||
this._dataset_changed = true;
|
||||
});
|
||||
this.dataset.x2m.on('load_record', this, function () {
|
||||
self._dataset_changed = false;
|
||||
this._dataset_changed = false;
|
||||
});
|
||||
|
||||
this.on('warning', this, function(e) { // In case of a one2many, we do not want any warning which comes from the editor
|
||||
e.stop_propagation();
|
||||
});
|
||||
},
|
||||
do_add_record: function () {
|
||||
@@ -1121,11 +1107,12 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
}
|
||||
var parent_form = this.x2m.view;
|
||||
var self = this;
|
||||
this.ensure_saved().then(function () {
|
||||
if (parent_form)
|
||||
this.save_edition().then(function () {
|
||||
if (parent_form) {
|
||||
return parent_form.save();
|
||||
else
|
||||
} else {
|
||||
return $.when();
|
||||
}
|
||||
}).done(function () {
|
||||
var ds = self.x2m.dataset;
|
||||
var changed_records = _.find(ds.cache, function(record) {
|
||||
@@ -1133,7 +1120,7 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
});
|
||||
if (!self.x2m.options.reload_on_button && !changed_records) {
|
||||
self.handle_button(name, id, callback);
|
||||
}else {
|
||||
} else {
|
||||
self.handle_button(name, id, function(){
|
||||
self.x2m.view.reload();
|
||||
});
|
||||
@@ -1149,9 +1136,12 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
reload_content: function () {
|
||||
var self = this;
|
||||
if (self.__focus) {
|
||||
self._on_blur_one2many(true);
|
||||
self._on_blur_one2many();
|
||||
return this._super().then(function () {
|
||||
self.start_edition(self.records.get(self.editor.form.datarecord.id));
|
||||
var record_being_edited = self.records.get(self.editor.form.datarecord.id);
|
||||
if (record_being_edited) {
|
||||
self.start_edition(record_being_edited);
|
||||
}
|
||||
});
|
||||
}
|
||||
return this._super();
|
||||
@@ -1161,31 +1151,52 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
this._dataset_changed = false;
|
||||
this.__focus = true;
|
||||
},
|
||||
_on_blur_one2many: function (force) {
|
||||
var self = this;
|
||||
var def = $.when();
|
||||
|
||||
if (this.__ignore_blur && !force) {
|
||||
this.__ignore_blur = false;
|
||||
_on_click_outside: function(e) {
|
||||
if(this.__ignore_blur || !this.editor.is_editing()) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $target = $(e.target);
|
||||
|
||||
// If click on a button, a ui-autocomplete dropdown or modal-backdrop, it is not considered as a click outside
|
||||
var click_outside = ($target.closest('.ui-autocomplete,.btn,.modal-backdrop').length === 0);
|
||||
|
||||
// Check if click inside the current list editable
|
||||
var $o2m = $target.closest(".oe_list_editable");
|
||||
if($o2m.length && $o2m[0] === this.el) {
|
||||
click_outside = false;
|
||||
}
|
||||
|
||||
// Check if click inside a modal which is on top of the current list editable
|
||||
var $modal = $target.closest(".modal");
|
||||
if($modal.length) {
|
||||
var $currentModal = this.$el.closest(".modal");
|
||||
if($currentModal.length === 0 || $currentModal[0] !== $modal[0]) {
|
||||
click_outside = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (click_outside) {
|
||||
this._on_blur_one2many();
|
||||
}
|
||||
},
|
||||
_on_blur_one2many: function() {
|
||||
if(this.__ignore_blur) {
|
||||
return $.when();
|
||||
}
|
||||
|
||||
this.__ignore_blur = true;
|
||||
this.__focus = false;
|
||||
this.dataset.x2m.internal_dataset_changed = false;
|
||||
|
||||
if (this.editor.form.is_dirty()) {
|
||||
def = this.ensure_saved();
|
||||
} else if (this.editor.record) {
|
||||
def = this.cancel_edition();
|
||||
}
|
||||
|
||||
def.then(function () {
|
||||
var self = this;
|
||||
return this.save_edition().done(function () {
|
||||
if (self._dataset_changed) {
|
||||
self.dataset.trigger('dataset_changed');
|
||||
}
|
||||
});
|
||||
|
||||
return def;
|
||||
}).always(function() {
|
||||
self.__ignore_blur = false;
|
||||
});
|
||||
},
|
||||
_after_edit: function () {
|
||||
this.editor.form.on('blurred', this, this._on_blur_one2many);
|
||||
@@ -1215,10 +1226,6 @@ var One2ManyListView = X2ManyListView.extend({
|
||||
|
||||
return this._super(record);
|
||||
},
|
||||
destroy: function () {
|
||||
this._super();
|
||||
$(document).off('mousedown', this._mousedown_blur_line);
|
||||
}
|
||||
});
|
||||
|
||||
var One2ManyGroups = ListView.Groups.extend({
|
||||
@@ -1656,7 +1663,6 @@ var FieldMany2ManyCheckBoxes = AbstractManyField.extend(common.ReinitializeField
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
core.form_widget_registry
|
||||
.add('many2one', FieldMany2One)
|
||||
.add('many2many', FieldMany2Many)
|
||||
@@ -1673,3 +1679,4 @@ return {
|
||||
};
|
||||
|
||||
});
|
||||
|
||||
|
||||
@@ -47,11 +47,6 @@ var Editor = Widget.extend({
|
||||
$buttons: $(),
|
||||
$pager: $(),
|
||||
});
|
||||
|
||||
// FIXME
|
||||
// this.on('warning', this, function(e) {
|
||||
// e.stop_propagation();
|
||||
// });
|
||||
},
|
||||
start: function () {
|
||||
var self = this;
|
||||
@@ -115,7 +110,7 @@ var Editor = Widget.extend({
|
||||
if(result.created && !self.record.id) {
|
||||
self.record.id = result.result;
|
||||
}
|
||||
return self.cancel();
|
||||
return self.record;
|
||||
});
|
||||
},
|
||||
cancel: function (force) {
|
||||
@@ -305,24 +300,13 @@ ListView.include(/** @lends instance.web.ListView# */{
|
||||
},
|
||||
do_button_action: function (name, id, callback) {
|
||||
var self = this;
|
||||
this.ensure_saved().done(function (data) {
|
||||
this.save_edition().done(function (data) {
|
||||
if(!id && data.created) {
|
||||
id = data.record.get('id');
|
||||
}
|
||||
self.handle_button(name, id, callback);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Ensures the editable list is saved (saves any pending edition if
|
||||
* needed, or tries to)
|
||||
*
|
||||
* Returns a deferred to the end of the saving.
|
||||
*
|
||||
* @returns {$.Deferred}
|
||||
*/
|
||||
ensure_saved: function () {
|
||||
return this.save_edition();
|
||||
},
|
||||
/**
|
||||
* Builds a record with the provided id (``false`` for a creation),
|
||||
* setting all columns with ``false`` value so code which relies on
|
||||
@@ -358,10 +342,8 @@ ListView.include(/** @lends instance.web.ListView# */{
|
||||
this.records.add(record, {at: (this.prepends_on_create())? 0 : null});
|
||||
}
|
||||
|
||||
return this.ensure_saved().then(function() {
|
||||
return this.save_edition().then(function() {
|
||||
return $.when.apply($, self.editor.form.render_value_defs);
|
||||
}, function() {
|
||||
return self.cancel_edition();
|
||||
}).then(function () {
|
||||
var $recordRow = self.groups.get_row_for(record);
|
||||
var cells = self.get_cells_for($recordRow);
|
||||
@@ -406,7 +388,7 @@ ListView.include(/** @lends instance.web.ListView# */{
|
||||
}
|
||||
});
|
||||
}, function() {
|
||||
return $.Deferred().resolve(); // Here the cancel edition failed so the start_edition is considered as done and succeeded
|
||||
return $.Deferred().resolve(); // Here the save/cancel edition failed so the start_edition is considered as done and succeeded
|
||||
});
|
||||
},
|
||||
get_cells_for: function ($row) {
|
||||
@@ -478,13 +460,18 @@ ListView.include(/** @lends instance.web.ListView# */{
|
||||
// onwrite callback could be altering & reloading the
|
||||
// record which has *just* been saved, so first perform all
|
||||
// onwrites then do a final reload of the record
|
||||
return self.handle_onwrite(record)
|
||||
return self.cancel_edition(true)
|
||||
.then(function() {
|
||||
return self.handle_onwrite(record);
|
||||
})
|
||||
.then(function () {
|
||||
return self.reload_record(record);
|
||||
})
|
||||
.then(function () {
|
||||
return {created: created, record: record};
|
||||
});
|
||||
}, function() {
|
||||
return self.cancel_edition();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -62,8 +62,7 @@ odoo.define_section('editor', ['web.ListEditor'], function (test, mock) {
|
||||
return e.appendTo($fix)
|
||||
.done(function () {
|
||||
ok(!e.is_editing(), "should not be editing");
|
||||
ok(e.form instanceof FormView,
|
||||
"should use default form type");
|
||||
ok(e.form instanceof FormView, "should use default form type");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -93,7 +92,9 @@ odoo.define_section('editor', ['web.ListEditor'], function (test, mock) {
|
||||
.then(function (form) {
|
||||
assert.ok(e.is_editing(), "should be editing");
|
||||
assert.equal(counter, 3, "should have configured all fields");
|
||||
return e.save();
|
||||
return e.save().then(function() {
|
||||
return e.cancel();
|
||||
});
|
||||
})
|
||||
.done(function (record) {
|
||||
assert.ok(!e.is_editing(), "should have stopped editing");
|
||||
@@ -133,7 +134,7 @@ odoo.define_section('editor', ['web.ListEditor'], function (test, mock) {
|
||||
assert.expect(2);
|
||||
|
||||
var warnings = 0;
|
||||
|
||||
|
||||
var e = new ListEditor({
|
||||
dataset: new data.DataSetSearch(null, 'test.model'),
|
||||
prepends_on_create: function () { return false; },
|
||||
@@ -157,7 +158,9 @@ odoo.define_section('editor', ['web.ListEditor'], function (test, mock) {
|
||||
});
|
||||
})
|
||||
.then(function (form) {
|
||||
return e.save();
|
||||
return e.save().then(function() {
|
||||
return e.cancel();
|
||||
});
|
||||
})
|
||||
.done(function () { assert.ok(false, "cancel should not succeed"); })
|
||||
.fail(function () {
|
||||
|
||||
Reference in New Issue
Block a user