[FIX] web: several minor issues with editable list

- correctly update the pager when a record is added or removed
- destroy the widgets of the removed row when the user clicks on
  'Cancel' when creating a new record
- ensure that the list always contains 4 rows (before this rev.,
  in list views with less than 4 records,   if the user created a
  new record, then clicked on 'Discard', there were only 3 rows
  left
- properly destroy field widgets when removed from the DOM

Note that the change of parent.parentID to parent.static is not a
bug fix, but it's better to use the key that indicates the type of
list element, instead of implying it from other attributes.
This commit is contained in:
Aaron Bohy
2017-09-20 17:07:04 +02:00
committed by Géry Debongnie
parent a5ca63c83b
commit 130c47bc0a
5 changed files with 79 additions and 14 deletions
@@ -768,7 +768,7 @@ var BasicModel = AbstractModel.extend({
removeLine: function (elementID) {
var record = this.localData[elementID];
var parent = this.localData[record.parentID];
if (parent.parentID) {
if (parent.static) {
// x2Many case: the new record has been stored in _changes, as a
// command so we remove the command(s) related to that record
parent._changes = _.filter(parent._changes, function (change) {
@@ -777,6 +777,7 @@ var BasicModel = AbstractModel.extend({
} else {
// main list view case: the new record is in data
parent.data = _.without(parent.data, elementID);
parent.count--;
}
},
/**
@@ -273,18 +273,18 @@ var BasicRenderer = AbstractRenderer.extend({
* from internal referencing.
*
* @private
* @param {Object} record
* @param {string} recordID id of the local resource
* @param {AbstractField} widget
* @returns {integer} the index of the removed widget
*/
_destroyFieldWidget: function (record, widget) {
var recordWidgets = this.allFieldWidgets[record.id];
_destroyFieldWidget: function (recordID, widget) {
var recordWidgets = this.allFieldWidgets[recordID];
var index = recordWidgets.indexOf(widget);
if (index >= 0) {
recordWidgets.splice(index, 1);
}
this._unregisterModifiersElement(widget.__node, record, widget);
widget.$el.destroy();
this._unregisterModifiersElement(widget.__node, recordID, widget);
widget.destroy();
return index;
},
/**
@@ -541,7 +541,7 @@ var BasicRenderer = AbstractRenderer.extend({
widget.$el.replaceWith(newWidget.$el);
// Destroy the old widget and position the new one at the old one's
var oldIndex = this._destroyFieldWidget(record, widget);
var oldIndex = this._destroyFieldWidget(record.id, widget);
var recordWidgets = this.allFieldWidgets[record.id];
recordWidgets.splice(oldIndex, 0, newWidget);
recordWidgets.pop();
@@ -553,13 +553,13 @@ var BasicRenderer = AbstractRenderer.extend({
* node and record.
*
* @param {Object} node
* @param {Object} record
* @param {string} recordID id of the local resource
* @param {jQuery|AbstractField} element
*/
_unregisterModifiersElement: function (node, record, element) {
_unregisterModifiersElement: function (node, recordID, element) {
var modifiersData = this._getModifiersData(node);
if (modifiersData) {
var elements = modifiersData.elementsByRecord[record.id];
var elements = modifiersData.elementsByRecord[recordID];
var index = _.findIndex(elements, function (oldElement) {
return oldElement.widget === element
|| oldElement.$el[0] === element[0];
@@ -182,6 +182,7 @@ var ListController = BasicController.extend({
if ((recordID || this.handle) !== this.handle) {
var state = this.model.get(this.handle);
this.renderer.removeLine(state, recordID);
this._updatePager();
}
},
/**
@@ -202,6 +203,7 @@ var ListController = BasicController.extend({
var state = self.model.get(self.handle);
self.renderer.updateState(state, {});
self.renderer.editRecord(recordID);
self._updatePager();
}).always(this._enableButtons.bind(this));
},
/**
@@ -133,6 +133,7 @@ ListRenderer.include({
* @param {string} recordID
*/
removeLine: function (state, recordID) {
var self = this;
var rowIndex = _.findIndex(this.state.data, {id: recordID});
this.state = state;
if (rowIndex === -1) {
@@ -141,8 +142,20 @@ ListRenderer.include({
if (rowIndex === this.currentRow) {
this.currentRow = null;
}
// remove the row
var $row = this.$('.o_data_row:nth(' + rowIndex + ')');
$row.remove();
if (this.state.count >= 4) {
$row.remove();
} else {
$row.replaceWith(this._renderEmptyRow());
}
// destroy the removed row's widgets
var widgetsToDestroy = this.allFieldWidgets[recordID].slice();
_.each(widgetsToDestroy, function (widget) {
self._destroyFieldWidget(recordID, widget);
});
},
/**
* Updates the already rendered row associated to the given recordID so that
@@ -221,7 +234,7 @@ ListRenderer.include({
// destroyed. This is not the case for simple buttons so we have to
// do it here.
if ($td.hasClass('o_list_button')) {
self._unregisterModifiersElement(node, record, $td.children());
self._unregisterModifiersElement(node, recordID, $td.children());
}
// For edit mode we only replace the content of the cell with its
@@ -231,14 +244,14 @@ ListRenderer.include({
if (editMode) {
$td.empty().append($newTd.contents());
} else {
self._unregisterModifiersElement(node, record, $td);
self._unregisterModifiersElement(node, recordID, $td);
$td.replaceWith($newTd);
}
});
delete this.defs;
// Destroy old field widgets
_.each(oldWidgets, this._destroyFieldWidget.bind(this, record));
_.each(oldWidgets, this._destroyFieldWidget.bind(this, recordID));
// Toggle selected class here so that style is applied at the end
$row.toggleClass('o_selected_row', editMode);
@@ -2,6 +2,7 @@ odoo.define('web.list_tests', function (require) {
"use strict";
var config = require('web.config');
var basicFields = require('web.basic_fields');
var FormView = require('web.FormView');
var ListView = require('web.ListView');
var testUtils = require('web.test_utils');
@@ -388,6 +389,54 @@ QUnit.module('Views', {
list.destroy();
});
QUnit.test('editable list: add a line and discard', function (assert) {
assert.expect(11);
var oldDestroy = basicFields.FieldChar.prototype.destroy;
basicFields.FieldChar.prototype.destroy = function () {
assert.step('destroy');
oldDestroy.apply(this, arguments);
};
var list = createView({
View: ListView,
model: 'foo',
data: this.data,
arch: '<tree editable="bottom"><field name="foo"/><field name="bar"/></tree>',
domain: [['foo', '=', 'yop']],
});
assert.strictEqual(list.$('tbody tr').length, 4,
"list should contain 4 rows");
assert.strictEqual(list.$('.o_data_row').length, 1,
"list should contain one record (and thus 3 empty rows)");
assert.strictEqual(list.pager.$('.o_pager_value').text(), '1-1',
"pager should be correct");
list.$buttons.find('.o_list_button_add').click();
assert.strictEqual(list.$('tbody tr').length, 4,
"list should still contain 4 rows");
assert.strictEqual(list.$('.o_data_row').length, 2,
"list should contain two record (and thus 2 empty rows)");
assert.strictEqual(list.pager.$('.o_pager_value').text(), '1-2',
"pager should be correct");
list.$buttons.find('.o_list_button_discard').click();
assert.strictEqual(list.$('tbody tr').length, 4,
"list should still contain 4 rows");
assert.strictEqual(list.$('.o_data_row').length, 1,
"list should contain one record (and thus 3 empty rows)");
assert.strictEqual(list.pager.$('.o_pager_value').text(), '1-1',
"pager should be correct");
assert.verifySteps(['destroy'],
"should have destroyed the widget of the removed line");
basicFields.FieldChar.prototype.destroy = oldDestroy;
list.destroy();
});
QUnit.test('field changes are triggered correctly', function (assert) {
assert.expect(2);