From bead8452c262e81597cd87269ea008991cd20fed Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 26 Apr 2017 09:18:15 +0200 Subject: [PATCH] [FIX] web: Views: sequential lazy loading of libs The views can specify js (and css) libraries to lazy load when they are instantiated for the first time. The previous implementation was too naive as it always loaded the specified libraries in parallel. However, it may happen (e.g. gantt view) that some of those libraries depend on other ones. This commit implements a mechanism to handle libs dependencies between each other. For instance, specifying: js_libs: [ ['a', 'b'], ['c'], ], will load 'a' and 'b' in parallel, but wait for them to be loaded before loading 'c'. The new implementation still supports the old syntax. So, js_libs: ['a', 'b', 'c'], will load 'a', 'b' and 'c' in parallel. --- .../web/static/src/js/views/abstract_view.js | 20 +++- .../static/tests/views/abstract_view_tests.js | 106 ++++++++++++++++++ addons/web/views/webclient_templates.xml | 1 + 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 addons/web/static/tests/views/abstract_view_tests.js diff --git a/addons/web/static/src/js/views/abstract_view.js b/addons/web/static/src/js/views/abstract_view.js index fdb029da6c3..f4def9f69c8 100644 --- a/addons/web/static/src/js/views/abstract_view.js +++ b/addons/web/static/src/js/views/abstract_view.js @@ -185,8 +185,24 @@ var AbstractView = Class.extend({ */ _loadLibs: function () { var defs = []; - _.each(this.config.js_libs, function (url) { - defs.push(ajax.loadJS(url)); + var jsDefs; + _.each(this.config.js_libs, function (urls) { + if (typeof(urls) === 'string') { + // js_libs is an array of urls: those urls can be loaded in + // parallel + defs.push(ajax.loadJS(urls)); + } else { + // js_libs is an array of arrays of urls: those arrays of urls + // must be loaded sequentially, but the urls inside each + // sub-array can be loaded in parallel + defs.push($.when.apply($, jsDefs).then(function () { + jsDefs = []; + _.each(urls, function (url) { + jsDefs.push(ajax.loadJS(url)); + }); + return $.when.apply($, jsDefs); + })); + } }); _.each(this.config.css_libs, function (url) { defs.push(ajax.loadCSS(url)); diff --git a/addons/web/static/tests/views/abstract_view_tests.js b/addons/web/static/tests/views/abstract_view_tests.js new file mode 100644 index 00000000000..1c85b390cc6 --- /dev/null +++ b/addons/web/static/tests/views/abstract_view_tests.js @@ -0,0 +1,106 @@ +odoo.define('web.abstract_view_tests', function (require) { +"use strict"; + +var AbstractView = require('web.AbstractView'); +var ajax = require('web.ajax'); +var testUtils = require('web.test_utils'); + +var createAsyncView = testUtils.createAsyncView; + +QUnit.module('Views', { + beforeEach: function () { + this.data = { + fake_model: { + fields: {}, + record: [], + }, + }; + }, +}, function () { + + QUnit.module('AbstractView'); + + QUnit.test('lazy loading of js libs (in parallel)', function (assert) { + var done = assert.async(); + assert.expect(6); + + var def = $.Deferred(); + var loadJS = ajax.loadJS; + ajax.loadJS = function (url) { + assert.step(url); + return def.then(function () { + assert.step(url + ' loaded'); + }); + }; + var View = AbstractView.extend({ + config: _.extend({}, AbstractView.prototype.config, { + js_libs: ['a', 'b'], + }), + }); + createAsyncView({ + View: View, + arch: '', + data: this.data, + model: 'fake_model', + }).then(function (view) { + assert.verifySteps(['a', 'b', 'a loaded', 'b loaded'], + "should wait for both libs to be loaded"); + ajax.loadJS = loadJS; + view.destroy(); + done(); + }); + + assert.verifySteps(['a', 'b'], + "both libs should be loaded in parallel"); + def.resolve(); + }); + + QUnit.test('lazy loading of js libs (sequentially)', function (assert) { + var done = assert.async(); + assert.expect(10); + + var defs = { + a: $.Deferred(), + b: $.Deferred(), + c: $.Deferred(), + }; + var loadJS = ajax.loadJS; + ajax.loadJS = function (url) { + assert.step(url); + return defs[url].then(function () { + assert.step(url + ' loaded'); + }); + }; + var View = AbstractView.extend({ + config: _.extend({}, AbstractView.prototype.config, { + js_libs: [ + ['a', 'b'], + ['c'], + ], + }), + }); + createAsyncView({ + View: View, + arch: '', + data: this.data, + model: 'fake_model', + }).then(function (view) { + assert.verifySteps(['a', 'b', 'a loaded', 'b loaded', 'c', 'c loaded'], + "should for all libs to be loaded"); + ajax.loadJS = loadJS; + view.destroy(); + done(); + }); + + assert.verifySteps(['a', 'b'], + "libs 'a' and 'b' should be loaded in parallel"); + defs.a.resolve(); + assert.verifySteps(['a', 'b', 'a loaded'], + "should wait for 'a' and 'b' to be loaded before loading 'c'"); + defs.b.resolve(); + assert.verifySteps(['a', 'b', 'a loaded', 'b loaded', 'c'], + "should load 'c' when 'a' and 'b' are loaded"); + defs.c.resolve(); + }); +}); +}); diff --git a/addons/web/views/webclient_templates.xml b/addons/web/views/webclient_templates.xml index 8eeb2c7eba5..27831759b4b 100644 --- a/addons/web/views/webclient_templates.xml +++ b/addons/web/views/webclient_templates.xml @@ -434,6 +434,7 @@ +