From 0cb57f52de3f81894cc8eebe6953e94df3dfd296 Mon Sep 17 00:00:00 2001 From: woodsaj Date: Tue, 8 Mar 2016 15:03:34 +0800 Subject: [PATCH] refactor how template vars are updated. fixes #4283 Use promises to order the updates of variable options so that parents are always updated before children. This ensures that we only need to query the datasource once per variable as variables that depend on other variables will only be processed once their parent has been. This commit also ensures that variable options are refreshed if "refresh_on_load" is true even when query params are used to set the variable seltion. --- .../features/templating/templateValuesSrv.js | 75 +++++++++++++++---- 1 file changed, 61 insertions(+), 14 deletions(-) diff --git a/public/app/features/templating/templateValuesSrv.js b/public/app/features/templating/templateValuesSrv.js index 46d2f90bc03..5256c3e59b5 100644 --- a/public/app/features/templating/templateValuesSrv.js +++ b/public/app/features/templating/templateValuesSrv.js @@ -27,29 +27,72 @@ function (angular, _, kbn) { var queryParams = $location.search(); var promises = []; + //use promises to delay processing variables that + //depend on other variables. + this.variableLock = {}; + var self = this; + _.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.processVariable = function(variable, queryParams) { + var dependencies = []; + var self = this; + // 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 variableName = variable.name; + var urlValue = queryParams['var-' + variable.name]; + if (urlValue !== void 0) { + return self.setVariableFromUrl(variable, urlValue).then(function() { + self.variableLock[variableName].resolve(); + }); + } + else if (variable.refresh) { + return self.updateOptions(variable).then(function() { + self.variableLock[variableName].resolve(); + }); + } + else if (variable.type === 'interval') { + self.updateAutoInterval(variable); + self.variableLock[variableName].resolve(); + } else { + self.variableLock[variableName].resolve(); + } + }); + }; + this.setVariableFromUrl = function(variable, urlValue) { + if (variable.refresh) { + var self = this; + //refresh the list of options before setting the value + return this.updateOptions(variable).then(function() { + var option = _.findWhere(variable.options, { text: urlValue }); + option = option || { text: urlValue, value: urlValue }; + + self.updateAutoInterval(variable); + return self.setVariableValue(variable, option, true); + }); + } var option = _.findWhere(variable.options, { text: urlValue }); option = option || { text: urlValue, value: urlValue }; this.updateAutoInterval(variable); - return this.setVariableValue(variable, option); + return this.setVariableValue(variable, option, true); }; this.updateAutoInterval = function(variable) { @@ -64,7 +107,7 @@ function (angular, _, kbn) { templateSrv.setGrafanaVariable('$__auto_interval', interval); }; - this.setVariableValue = function(variable, option) { + this.setVariableValue = function(variable, option, firstLoad) { variable.current = angular.copy(option); if (_.isArray(variable.current.value)) { @@ -74,6 +117,11 @@ function (angular, _, kbn) { self.selectOptionsForCurrentValue(variable); templateSrv.updateTemplateData(); + // on first load, variable loading is ordered to ensure + // that parents are updated before children. + if (firstLoad) { + return $q.when(); + } return self.updateOptionsInChildVariables(variable); }; @@ -119,8 +167,7 @@ function (angular, _, kbn) { return datasourceSrv.get(variable.datasource) .then(_.partial(this.updateOptionsFromMetricFindQuery, variable)) - .then(_.partial(this.updateTags, variable)) - .then(_.partial(this.validateVariableSelectionState, variable)); + .then(_.partial(this.updateTags, variable)); }; this.selectOptionsForCurrentValue = function(variable) {