Rudimentary multi pass dependency resolution for view models
Largely based on a patch by @mrbeam - big thanks for that! Conflicts: src/octoprint/static/js/app/main.js
This commit is contained in:
parent
63f7b21f5b
commit
70a75863ab
1 changed files with 76 additions and 43 deletions
|
|
@ -77,6 +77,7 @@ $(function() {
|
|||
var logViewModel = new LogViewModel(loginStateViewModel);
|
||||
|
||||
|
||||
// the view model map is our basic look up table for dependencies that may be injected into other view models
|
||||
var viewModelMap = {
|
||||
loginStateViewModel: loginStateViewModel,
|
||||
printerProfilesViewModel: printerProfilesViewModel,
|
||||
|
|
@ -96,53 +97,85 @@ $(function() {
|
|||
slicingViewModel: slicingViewModel,
|
||||
};
|
||||
|
||||
var _createViewModelInstance = function(viewModel, viewModelMap){
|
||||
var viewModelClass = viewModel[0];
|
||||
// helper to create a view model instance with injected constructor parameters from the view model map
|
||||
var _createViewModelInstance = function(viewModel, viewModelMap){
|
||||
var viewModelClass = viewModel[0];
|
||||
var viewModelParameters = viewModel[1];
|
||||
|
||||
var constructorParameters = [];
|
||||
for (var idx = 0; idx < viewModelParameters.length; idx++) {
|
||||
var parameter = viewModelParameters[idx];
|
||||
// now we'll try to resolve all of the view model's constructor parameters via our view model map
|
||||
var constructorParameters = _.map(viewModelParameters, function(parameter){
|
||||
return viewModelMap[parameter]
|
||||
});
|
||||
|
||||
if (_.has(viewModelMap, parameter)) {
|
||||
constructorParameters.push(viewModelMap[parameter]);
|
||||
} else {
|
||||
console.warn("postponing", viewModel[0].name, 'missing param: ', parameter);
|
||||
return;
|
||||
}
|
||||
}
|
||||
var viewModelInstance = new viewModelClass(constructorParameters);
|
||||
return viewModelInstance;
|
||||
};
|
||||
|
||||
var _getViewModelId = function(viewModel){
|
||||
var name = viewModel[0].name;
|
||||
return name.substr(0, 1).toLowerCase() + name.substr(1);
|
||||
};
|
||||
|
||||
var vmtmp = ADDITIONAL_VIEWMODELS.slice();
|
||||
if (_.some(constructorParameters, function(parameter) { return parameter === undefined; })) {
|
||||
var _extractName = function(entry) { return entry[0]; };
|
||||
var _onlyUnresolved = function(entry) { return entry[1] === undefined; };
|
||||
var missingParameters = _.map(_.filter(_.zip(viewModelParameters, constructorParameters), _onlyUnresolved), _extractName);
|
||||
console.log("postponing", viewModel[0].name, "due to missing parameters:", missingParameters.join(", "));
|
||||
return;
|
||||
}
|
||||
|
||||
// if we came this far then we could resolve all constructor parameters, so let's construct that view model
|
||||
return new viewModelClass(constructorParameters);
|
||||
};
|
||||
|
||||
// helper for translating the name of a view model class into an identifier for the view model map
|
||||
var _getViewModelId = function(viewModel){
|
||||
var name = viewModel[0].name;
|
||||
return name.substr(0, 1).toLowerCase() + name.substr(1); // FooBarViewModel => fooBarViewModel
|
||||
};
|
||||
|
||||
// instantiation loop, will make multiple passes over the list of unprocessed view models until all
|
||||
// view models have been successfully instantiated with all of their dependencies or no changes can be made
|
||||
// any more which means not all view models can be instantiated due to missing dependencies
|
||||
var unprocessedViewModels = ADDITIONAL_VIEWMODELS.slice();
|
||||
var additionalViewModels = [];
|
||||
var attempt = 0;
|
||||
while(vmtmp.length > 0 && attempt < 3){
|
||||
while(vmtmp.length > 0){
|
||||
var viewModel = vmtmp.shift();
|
||||
var viewModelInstance = _createViewModelInstance(viewModel, viewModelMap);
|
||||
if(viewModelInstance !== undefined){
|
||||
var viewModelBindTarget = viewModel[2];
|
||||
var viewModelId = _getViewModelId(viewModel);
|
||||
if(viewModelMap[viewModelId] !== undefined){
|
||||
console.error("Duplicate class name while instantiating viewModel ", viewModelId);
|
||||
} else {
|
||||
additionalViewModels.push([viewModelInstance, viewModelBindTarget]);
|
||||
viewModelMap[viewModelId] = viewModelInstance;
|
||||
}
|
||||
} else {
|
||||
vmtmp.push(viewModel);
|
||||
}
|
||||
}
|
||||
attempt++;
|
||||
}
|
||||
|
||||
var pass = 1;
|
||||
while (unprocessedViewModels.length > 0) {
|
||||
console.log("View model dependency resolution, pass #" + pass++);
|
||||
var startLength = unprocessedViewModels.length;
|
||||
var postponed = [];
|
||||
|
||||
// now try to instantiate every one of our as of yet unprocessed view model descriptors
|
||||
while (unprocessedViewModels.length > 0){
|
||||
var viewModel = unprocessedViewModels.shift();
|
||||
var viewModelId = _getViewModelId(viewModel);
|
||||
|
||||
// make sure that we don't have to view models going by the same name
|
||||
if (_.has(viewModelMap, viewModelId)) {
|
||||
console.error("Duplicate class name while instantiating viewModel ", viewModelId);
|
||||
continue;
|
||||
}
|
||||
|
||||
var viewModelInstance = _createViewModelInstance(viewModel, viewModelMap);
|
||||
|
||||
// our view model couldn't yet be instantiated, so postpone it for a bit
|
||||
if (viewModelInstance === undefined) {
|
||||
postponed.push(viewModel);
|
||||
continue;
|
||||
}
|
||||
|
||||
// we could resolve the depdendencies and the view model is not defined yet => add it, it's now fully processed
|
||||
var viewModelBindTarget = viewModel[2];
|
||||
additionalViewModels.push([viewModelInstance, viewModelBindTarget]);
|
||||
viewModelMap[viewModelId] = viewModelInstance;
|
||||
}
|
||||
|
||||
// anything that's now in the postponed list has to be readded to the unprocessedViewModels
|
||||
unprocessedViewModels = unprocessedViewModels.concat(postponed);
|
||||
|
||||
// if we still have the same amount of items in our list of unprocessed view models it means that we
|
||||
// couldn't instantiate any more view models over a whole iteration, which in turn mean we can't resolve the
|
||||
// dependencies of remaining ones, so log that as an error and then quit the loop
|
||||
if (unprocessedViewModels.length == startLength) {
|
||||
console.error("Could not instantiate the following view models due to unresolvable dependencies:");
|
||||
_.each(unprocessedViewModels, function(entry) {
|
||||
console.error(entry[0].name, "(missing:", _.filter(entry[1], function(id) { return !_.has(viewModelMap, id); }).join(", "), ")");
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
console.log("View model dependency resolution done");
|
||||
|
||||
var allViewModels = _.values(viewModelMap);
|
||||
var dataUpdater = new DataUpdater(allViewModels);
|
||||
|
|
|
|||
Loading…
Reference in a new issue