diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index 46d2f90bc03..51895ff1ebf 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -27,29 +27,71 @@ function (angular, _, kbn) { var queryParams = $location.search(); var promises = []; + // use promises to delay processing variables that + // depend on other variables. + this.variableLock = {}; + _.forEach(this.variables, function(variable) { + self.variableLock[variable.name] = $q.defer(); + }); + for (var i = 0; i < this.variables.length; i++) { var variable = this.variables[i]; - var urlValue = queryParams['var-' + variable.name]; - if (urlValue !== void 0) { - promises.push(this.setVariableFromUrl(variable, urlValue)); - } - else if (variable.refresh) { - promises.push(this.updateOptions(variable)); - } - else if (variable.type === 'interval') { - this.updateAutoInterval(variable); - } + promises.push(this.processVariable(variable, queryParams)); } return $q.all(promises); }; - this.setVariableFromUrl = function(variable, urlValue) { - var option = _.findWhere(variable.options, { text: urlValue }); - option = option || { text: urlValue, value: urlValue }; + this.processVariable = function(variable, queryParams) { + var dependencies = []; + var lock = self.variableLock[variable.name]; - this.updateAutoInterval(variable); - return this.setVariableValue(variable, option); + // determine our dependencies. + if (variable.type === "query") { + _.forEach(this.variables, function(v) { + if (templateSrv.containsVariable(variable.query, v.name)) { + dependencies.push(self.variableLock[v.name].promise); + } + }); + } + + return $q.all(dependencies).then(function() { + var urlValue = queryParams['var-' + variable.name]; + if (urlValue !== void 0) { + return self.setVariableFromUrl(variable, urlValue).then(lock.resolve); + } + else if (variable.refresh) { + return self.updateOptions(variable).then(function() { + if (_.isEmpty(variable.current) && variable.options.length) { + console.log("setting current for %s", variable.name); + self.setVariableValue(variable, variable.options[0]); + } + lock.resolve(); + }); + } + else if (variable.type === 'interval') { + self.updateAutoInterval(variable); + lock.resolve(); + } else { + lock.resolve(); + } + }); + }; + + this.setVariableFromUrl = function(variable, urlValue) { + var promise = $q.when(true); + + if (variable.refresh) { + promise = this.updateOptions(variable); + } + + return promise.then(function() { + var option = _.findWhere(variable.options, { text: urlValue }); + option = option || { text: urlValue, value: urlValue }; + + self.updateAutoInterval(variable); + return self.setVariableValue(variable, option, true); + }); }; this.updateAutoInterval = function(variable) { @@ -64,7 +106,7 @@ function (angular, _, kbn) { templateSrv.setGrafanaVariable('$__auto_interval', interval); }; - this.setVariableValue = function(variable, option) { + this.setVariableValue = function(variable, option, initPhase) { variable.current = angular.copy(option); if (_.isArray(variable.current.value)) { @@ -72,8 +114,14 @@ function (angular, _, kbn) { } self.selectOptionsForCurrentValue(variable); - templateSrv.updateTemplateData(); + + // on first load, variable loading is ordered to ensure + // that parents are updated before children. + if (initPhase) { + return $q.when(); + } + return self.updateOptionsInChildVariables(variable); }; @@ -145,7 +193,7 @@ function (angular, _, kbn) { this.validateVariableSelectionState = function(variable) { if (!variable.current) { if (!variable.options.length) { return; } - return self.setVariableValue(variable, variable.options[0]); + return self.setVariableValue(variable, variable.options[0], true); } if (_.isArray(variable.current.value)) { @@ -153,7 +201,7 @@ function (angular, _, kbn) { } else { var currentOption = _.findWhere(variable.options, { text: variable.current.text }); if (currentOption) { - return self.setVariableValue(variable, currentOption); + return self.setVariableValue(variable, currentOption, true); } else { if (!variable.options.length) { return; } return self.setVariableValue(variable, variable.options[0]); diff --git a/public/test/specs/templateValuesSrv-specs.js b/public/test/specs/templateValuesSrv-specs.js index 0eaa9d1d99f..d81ce4fd059 100644 --- a/public/test/specs/templateValuesSrv-specs.js +++ b/public/test/specs/templateValuesSrv-specs.js @@ -34,12 +34,13 @@ define([ options: [{text: "test", value: "test"}] }; - beforeEach(function() { + beforeEach(function(done) { var dashboard = { templating: { list: [variable] } }; var urlParams = {}; urlParams["var-apps"] = "new"; ctx.$location.search = sinon.stub().returns(urlParams); - ctx.service.init(dashboard); + ctx.service.init(dashboard).then(function() { done(); }); + ctx.$rootScope.$digest(); }); it('should update current value', function() { @@ -56,12 +57,13 @@ define([ options: [{text: "val1", value: "val1"}, {text: 'val2', value: 'val2'}, {text: 'val3', value: 'val3', selected: true}] }; - beforeEach(function() { + beforeEach(function(done) { var dashboard = { templating: { list: [variable] } }; var urlParams = {}; urlParams["var-apps"] = ["val2", "val1"]; ctx.$location.search = sinon.stub().returns(urlParams); - ctx.service.init(dashboard); + ctx.service.init(dashboard).then(function() { done(); }); + ctx.$rootScope.$digest(); }); it('should update current value', function() {