Faster module loading & vnAgencyCalendar bug fixes
gitea/salix/dev This commit looks good Details
gitea/salix/test This commit looks good Details

This commit is contained in:
Juan Ferrer 2019-11-01 13:02:47 +01:00
parent 4fcaa2e507
commit 026c608f42
7 changed files with 165 additions and 70 deletions

View File

@ -94,7 +94,7 @@ describe('Component vnDialog', () => {
expect(controller.onAccept).toHaveBeenCalledWith({$response: 'accept'});
});
it(`should resolve the promise returned by show() with response`, () => {
it(`should resolve the promise returned by show() with response when hidden`, () => {
let response;
controller.show().then(res => response = res);
controller.respond('response');

View File

@ -1,24 +1,23 @@
import isEqual from './equals';
export default function getModifiedData(object, objectOld) {
var newObject = {};
let newObject = {};
if (objectOld === null)
return object;
for (var k in object) {
var val = object[k];
var valOld = objectOld[k] === undefined ? null : objectOld[k];
for (let k in object) {
let val = object[k];
let valOld = objectOld[k] === undefined ? null : objectOld[k];
if (!isEqual(val, valOld)) {
if (val instanceof Date) {
if (val instanceof Date)
newObject[k] = new Date(val.getTime());
} else if (val instanceof Object) {
else if (val instanceof Object)
newObject[k] = getModifiedData(val, valOld);
} else {
else
newObject[k] = val;
}
}
}
return Object.keys(newObject).length ? newObject : undefined;
}

View File

@ -3,64 +3,80 @@ import moduleImport from 'module-import';
factory.$inject = ['$http', '$window', '$ocLazyLoad', '$translatePartialLoader', '$translate', '$q'];
export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $translate, $q) {
/**
* Used to load application modules lazily.
*/
class ModuleLoader {
constructor() {
this._loaded = {};
this.loaded = {};
this.imports = {};
this.moduleImport = moduleImport;
this.modelInfo = $http.get(`modelInfo`)
.then(json => {
this.onModelInfoReady(json);
this.modelInfo = true;
});
}
load(moduleName, validations) {
let moduleConf = $window.routes.find(i => i && i.module == moduleName);
/**
* Loads the passed module and it's dependencies. Loading a module
* implies load the webpack chunk, translations, recursively load
* module dependencies and finally register all of them into Angular.
*
* @param {String} mod The module name to load
* @return {Promise} Will be resolved when loaded, when module is
* already loaded it returns a resolved promise
*/
load(mod) {
let mods = [];
return this.loadRec(mod, mods);
}
loadRec(mod, mods) {
let loaded = this.loaded[mod];
if (loaded === true || mods.indexOf(mod) != -1)
return $q.resolve(true);
if (loaded instanceof $q)
return loaded;
let moduleConf = $window.routes.find(i => i && i.module == mod);
if (!moduleConf)
return $q.reject(new Error(`Module not found: ${moduleName}`));
return $q.reject(new Error(`Module not found: ${mod}`));
let loaded = this._loaded;
let promises = [];
if (loaded[moduleName] === true)
return Promise.resolve(true);
if (loaded[moduleName] instanceof Promise)
return loaded[moduleName];
if (loaded[moduleName] === false)
return Promise.resolve(true);
if (this.modelInfo instanceof $q)
promises.push(this.modelInfo);
loaded[moduleName] = false;
$translatePartialLoader.addPart(mod);
promises.push($translate.refresh());
let modImport = this.imports[mod];
if (!modImport) {
modImport = this.imports[mod] = this.moduleImport(mod)
.then(() => this.imports[mod] = true);
}
if (modImport && modImport.then)
promises.push(modImport);
let depPromises = [];
let deps = moduleConf.dependencies;
if (deps) {
mods.push(mod);
for (let dep of deps)
depPromises.push(this.load(dep, validations));
promises.push(this.loadRec(dep, mods));
mods.pop();
}
loaded[moduleName] = new Promise((resolve, reject) => {
Promise.all(depPromises).then(() => {
let promises = [];
$translatePartialLoader.addPart(moduleName);
promises.push(new Promise(resolve => {
$translate.refresh().then(resolve, resolve);
}));
if (validations) {
promises.push(new Promise(resolve => {
$http.get(`/${moduleName}/api/modelInfo`).then(
json => this.onValidationsReady(json, resolve),
() => resolve()
);
}));
this.loaded[mod] = $q.all(promises)
.then(() => $ocLazyLoad.load({name: mod}))
.then(() => this.loaded[mod] = true);
return this.loaded[mod];
}
promises.push(moduleImport(moduleName));
Promise.all(promises).then(() => {
loaded[moduleName] = true;
resolve($ocLazyLoad.load({name: moduleName}));
}).catch(reject);
}).catch(reject);
});
return loaded[moduleName];
}
onValidationsReady(json, resolve) {
onModelInfoReady(json) {
let entities = json.data;
for (let entity in entities) {
let fields = entities[entity].validations;
@ -72,12 +88,13 @@ export function factory($http, $window, $ocLazyLoad, $translatePartialLoader, $t
}
Object.assign($window.validations, json.data);
resolve();
}
parseValidation(val) {
switch (val.validation) {
case 'custom':
// TODO: Replace eval
// TODO: Don't use eval() because it's "evil".
// How to do the same without eval?
val.bindedFunction = eval(`(${val.bindedFunction})`);
break;
case 'format':

View File

@ -1,30 +1,108 @@
describe('factory vnModuleLoader', () => {
let vnModuleLoader;
let $rootScope;
let $window;
beforeEach(ngModule('vnCore'));
beforeEach(angular.mock.inject((_vnModuleLoader_, $rootScope, $window) => {
beforeEach(angular.mock.inject((_vnModuleLoader_, _$rootScope_, $httpBackend, _$window_, $q) => {
vnModuleLoader = _vnModuleLoader_;
$window.routes = [{module: 'myModule'}];
$rootScope = _$rootScope_;
$window = _$window_;
$window.validations = {};
$window.routes = [
{
module: 'myModule',
dependencies: ['fooModule', 'barModule']
}, {
module: 'fooModule',
dependencies: ['myModule']
}, {
module: 'barModule'
}
];
$httpBackend.whenGET('modelInfo')
.respond({
FooModel: {
properties: {
id: {type: 'Number'},
email: {type: 'String'},
field: {type: 'Boolean'}
},
validations: {
id: [{
validation: 'presence'
}],
email: [{
validation: 'format',
with: '/@/'
}],
field: [{
validation: 'custom',
bindedFunction: '() => true'
}]
}
}
});
$httpBackend.flush();
vnModuleLoader.moduleImport = () => $q.resolve();
}));
describe('load()', () => {
it('should return truthy promise if the module was loaded', async() => {
vnModuleLoader._loaded.myModule = true;
it('should throw error if module does not exist', async() => {
let errorThrown;
let result = await vnModuleLoader.load('myModule', {myValidations: () => {}});
vnModuleLoader.load('unexistentModule')
.catch(() => errorThrown = true);
$rootScope.$apply();
expect(result).toEqual(true);
expect(errorThrown).toBeTruthy();
});
it('should return a promise if the module was still a promise', () => {
vnModuleLoader._loaded.myModule = new Promise(() => {
return 'I promise you a module!';
it('should set module loaded to true when it is loaded', async() => {
vnModuleLoader.load('barModule');
$rootScope.$apply();
expect(vnModuleLoader.loaded['barModule']).toBeTruthy();
});
let result = vnModuleLoader.load('myModule', {myValidations: () => {}});
it('should resolve returned promise when module is loaded', async() => {
let loaded;
expect(result).toEqual(jasmine.any(Promise));
vnModuleLoader.load('barModule')
.then(() => loaded = true);
$rootScope.$apply();
expect(loaded).toBeTruthy();
});
it('should load dependencies', async() => {
vnModuleLoader.load('fooModule');
$rootScope.$apply();
expect(vnModuleLoader.loaded['barModule']).toBeTruthy();
});
it('should work with circular dependencies', async() => {
vnModuleLoader.load('myModule');
$rootScope.$apply();
expect(vnModuleLoader.loaded['fooModule']).toBeTruthy();
});
it('should load models information and parse validations', async() => {
vnModuleLoader.load('barModule');
let FooModel = $window.validations.FooModel;
let validations = FooModel && FooModel.validations;
expect(FooModel).toBeDefined();
expect(validations).toBeDefined();
expect(validations.email[0].with).toBeInstanceOf(RegExp);
expect(validations.field[0].bindedFunction).toBeInstanceOf(Function);
});
});
});

View File

@ -8,9 +8,11 @@ export default class Modules {
$window
});
}
reset() {
this.modules = null;
}
get() {
if (this.modules)
return this.modules;

View File

@ -1,10 +1,10 @@
import ngModule from './module';
import getMainRoute from 'core/lib/get-main-route';
function loader(moduleName, validations) {
function loader(moduleName) {
load.$inject = ['vnModuleLoader'];
function load(moduleLoader) {
return moduleLoader.load(moduleName, validations);
return moduleLoader.load(moduleName);
}
return load;
}
@ -31,7 +31,6 @@ function config($stateProvider, $urlRouterProvider) {
if (!route.params)
return params;
Object.keys(route.params).forEach(key => {
temporalParams.push(`${key} = "${route.params[key]}"`);
});

View File

@ -91,7 +91,7 @@ class Controller extends Component {
this.days = {};
let day = new Date(this.firstDay.getTime());
while (day < this.lastDay) {
while (day <= this.lastDay) {
let stamp = day.getTime();
let wday = day.getDay();
let dayEvents = [];