[IMP] web: invalidate JS cache for some models
Models concerned are 'ir.ui.view', 'ir.actions.act_window' and 'ir.filters'. The DataManager's cache is invalidated at each create, update and unlink operation. This cache stores actions (results of calls to /web/action/load route), views (results of load_views RPCs) and filters (fetched alonside views). This rev. thus spares the user from reloading the webclient after performing such operations on these models to see the changes in the interface, as he only has to replay the action to see the changes.
This commit is contained in:
@@ -134,6 +134,14 @@ var x2ManyCommands = {
|
||||
};
|
||||
|
||||
var BasicModel = AbstractModel.extend({
|
||||
// list of models for which the DataManager's cache should be cleared on
|
||||
// create, update and delete operations
|
||||
noCacheModels: [
|
||||
'ir.actions.act_window',
|
||||
'ir.filters',
|
||||
'ir.ui.view',
|
||||
],
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
@@ -248,6 +256,8 @@ var BasicModel = AbstractModel.extend({
|
||||
record.count--;
|
||||
}
|
||||
});
|
||||
// optionally clear the DataManager's cache
|
||||
self._invalidateCache(records[0]);
|
||||
});
|
||||
},
|
||||
/**
|
||||
@@ -909,11 +919,14 @@ var BasicModel = AbstractModel.extend({
|
||||
// Erase changes as they have been applied
|
||||
record._changes = {};
|
||||
|
||||
// Optionally clear the DataManager's cache
|
||||
self._invalidateCache(record);
|
||||
|
||||
self.unfreezeOrder(record.id);
|
||||
|
||||
// Update the data directly or reload them
|
||||
if (shouldReload) {
|
||||
self._fetchRecord(record).then(function (record) {
|
||||
self._fetchRecord(record).then(function () {
|
||||
def.resolve(changedFields);
|
||||
});
|
||||
} else {
|
||||
@@ -1023,8 +1036,11 @@ var BasicModel = AbstractModel.extend({
|
||||
method: 'write',
|
||||
args: [resIDs, { active: value }],
|
||||
})
|
||||
.then(this.reload.bind(this, parentID));
|
||||
|
||||
.then(function () {
|
||||
// optionally clear the DataManager's cache
|
||||
self._invalidateCache(parent);
|
||||
return self.reload(parentID);
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Toggle (open/close) a group in a grouped list, then fetches relevant
|
||||
@@ -2937,6 +2953,21 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
return context;
|
||||
},
|
||||
/**
|
||||
* Invalidates the DataManager's cache if the main model (i.e. the model of
|
||||
* its root parent) of the given dataPoint is a model in 'noCacheModels'.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} dataPoint
|
||||
*/
|
||||
_invalidateCache: function (dataPoint) {
|
||||
while (dataPoint.parentID) {
|
||||
dataPoint = this.localData[dataPoint.parentID];
|
||||
}
|
||||
if (_.contains(this.noCacheModels, dataPoint.model)) {
|
||||
core.bus.trigger('clear_cache');
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Returns true if the field is protected against changes, looking for a
|
||||
* readonly modifier unless there is a force_save modifier (checking first
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
odoo.define('web.form_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var BasicModel = require('web.BasicModel');
|
||||
var concurrency = require('web.concurrency');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var fieldRegistry = require('web.field_registry');
|
||||
var FormView = require('web.FormView');
|
||||
var pyeval = require('web.pyeval');
|
||||
var RainbowMan = require('web.RainbowMan');
|
||||
var testUtils = require('web.test_utils');
|
||||
var widgetRegistry = require('web.widget_registry');
|
||||
var Widget = require('web.Widget');
|
||||
@@ -6338,5 +6338,114 @@ QUnit.module('Views', {
|
||||
form.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('edition in form view on a "noCache" model', function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
testUtils.patch(BasicModel, {
|
||||
noCacheModels: BasicModel.prototype.noCacheModels.concat(['partner']),
|
||||
});
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
res_id: 1,
|
||||
viewOptions: {
|
||||
mode: 'edit',
|
||||
},
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'write') {
|
||||
assert.step('write');
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache'));
|
||||
|
||||
form.$('.o_field_widget[name=display_name]').val('new value').trigger('input');
|
||||
form.$buttons.find('.o_form_button_save').click();
|
||||
|
||||
assert.verifySteps(['write', 'clear_cache']);
|
||||
|
||||
form.destroy();
|
||||
testUtils.unpatch(BasicModel);
|
||||
});
|
||||
|
||||
QUnit.test('creation in form view on a "noCache" model', function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
testUtils.patch(BasicModel, {
|
||||
noCacheModels: BasicModel.prototype.noCacheModels.concat(['partner']),
|
||||
});
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'create') {
|
||||
assert.step('create');
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
});
|
||||
core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache'));
|
||||
|
||||
form.$('.o_field_widget[name=display_name]').val('value').trigger('input');
|
||||
form.$buttons.find('.o_form_button_save').click();
|
||||
|
||||
assert.verifySteps(['create', 'clear_cache']);
|
||||
|
||||
form.destroy();
|
||||
testUtils.unpatch(BasicModel);
|
||||
});
|
||||
|
||||
QUnit.test('deletion in form view on a "noCache" model', function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
testUtils.patch(BasicModel, {
|
||||
noCacheModels: BasicModel.prototype.noCacheModels.concat(['partner']),
|
||||
});
|
||||
|
||||
var form = createView({
|
||||
View: FormView,
|
||||
model: 'partner',
|
||||
data: this.data,
|
||||
arch: '<form>' +
|
||||
'<sheet>' +
|
||||
'<field name="display_name"/>' +
|
||||
'</sheet>' +
|
||||
'</form>',
|
||||
mockRPC: function (route, args) {
|
||||
if (args.method === 'unlink') {
|
||||
assert.step('unlink');
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
viewOptions: {
|
||||
hasSidebar: true,
|
||||
},
|
||||
});
|
||||
core.bus.on('clear_cache', form, assert.step.bind(assert, 'clear_cache'));
|
||||
|
||||
form.sidebar.$('a:contains(Delete)').click();
|
||||
$('.modal .modal-footer .btn-primary').click(); // confirm
|
||||
|
||||
assert.verifySteps(['unlink', 'clear_cache']);
|
||||
|
||||
form.destroy();
|
||||
testUtils.unpatch(BasicModel);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
odoo.define('web.list_tests', function (require) {
|
||||
"use strict";
|
||||
|
||||
var BasicModel = require('web.BasicModel');
|
||||
var config = require('web.config');
|
||||
var core = require('web.core');
|
||||
var basicFields = require('web.basic_fields');
|
||||
var FormView = require('web.FormView');
|
||||
var ListView = require('web.ListView');
|
||||
@@ -2351,7 +2353,6 @@ QUnit.module('Views', {
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('navigation with tab on a one2many list with create="0"', function (assert) {
|
||||
assert.expect(4);
|
||||
|
||||
@@ -3172,7 +3173,6 @@ QUnit.module('Views', {
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
|
||||
QUnit.test('basic support for widgets', function (assert) {
|
||||
assert.expect(1);
|
||||
|
||||
@@ -3223,6 +3223,59 @@ QUnit.module('Views', {
|
||||
list.destroy();
|
||||
});
|
||||
|
||||
QUnit.test('list view on a "noCache" model', function (assert) {
|
||||
assert.expect(8);
|
||||
|
||||
testUtils.patch(BasicModel, {
|
||||
noCacheModels: BasicModel.prototype.noCacheModels.concat(['foo']),
|
||||
});
|
||||
|
||||
var list = createView({
|
||||
View: ListView,
|
||||
model: 'foo',
|
||||
data: this.data,
|
||||
arch: '<tree editable="top">' +
|
||||
'<field name="display_name"/>' +
|
||||
'</tree>',
|
||||
mockRPC: function (route, args) {
|
||||
if (_.contains(['create', 'unlink', 'write'], args.method)) {
|
||||
assert.step(args.method);
|
||||
}
|
||||
return this._super.apply(this, arguments);
|
||||
},
|
||||
viewOptions: {
|
||||
hasSidebar: true,
|
||||
},
|
||||
});
|
||||
core.bus.on('clear_cache', list, assert.step.bind(assert, 'clear_cache'));
|
||||
|
||||
// create a new record
|
||||
list.$buttons.find('.o_list_button_add').click();
|
||||
list.$('.o_selected_row .o_field_widget').val('some value').trigger('input');
|
||||
list.$buttons.find('.o_list_button_save').click();
|
||||
|
||||
// edit an existing record
|
||||
list.$('.o_data_cell:first').click();
|
||||
list.$('.o_selected_row .o_field_widget').val('new value').trigger('input');
|
||||
list.$buttons.find('.o_list_button_save').click();
|
||||
|
||||
// delete a record
|
||||
list.$('.o_data_row:first .o_list_record_selector input').click();
|
||||
list.sidebar.$('a:contains(Delete)').click();
|
||||
$('.modal .modal-footer .btn-primary').click(); // confirm
|
||||
|
||||
assert.verifySteps([
|
||||
'create',
|
||||
'clear_cache',
|
||||
'write',
|
||||
'clear_cache',
|
||||
'unlink',
|
||||
'clear_cache',
|
||||
]);
|
||||
|
||||
list.destroy();
|
||||
testUtils.unpatch(BasicModel);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user