Prechádzať zdrojové kódy

Worked on variable initilization and sync to from url, #772

Torkel Ödegaard 11 rokov pred
rodič
commit
e0c9ddbfba

+ 2 - 0
CHANGELOG.md

@@ -13,6 +13,8 @@
 - [Issue #262](https://github.com/grafana/grafana/issues/262). Templating: Ability to use template variables for function parameters via custom variable type, can be used as parameter for movingAverage or scaleToSeconds for example
 - [Issue #312](https://github.com/grafana/grafana/issues/312). Templating: Can now use template variables in panel titles
 - [Issue #613](https://github.com/grafana/grafana/issues/613). Templating: Full support for InfluxDB, filter by part of series names, extract series substrings, nested queries, multipe where clauses!
+- Template variables can be initialized from url, with var-my_varname=value, breaking change, before it was just my_varname.
+- Templating and url state sync has some issues that are not solved for this release, see [Issue #772](https://github.com/grafana/grafana/issues/772) for more details.
 
 **InfluxDB Breaking changes**
 - To better support templating, fill(0) and group by time low limit some changes has been made to the editor and query model schema

+ 1 - 1
src/app/controllers/dashboardCtrl.js

@@ -51,7 +51,7 @@ function (angular, $, config, _) {
 
       // init services
       timeSrv.init($scope.dashboard);
-      templateValuesSrv.init($scope.dashboard);
+      templateValuesSrv.init($scope.dashboard, $scope.dashboardViewState);
       panelMoveSrv.init($scope.dashboard, $scope);
 
       $scope.checkFeatureToggles();

+ 1 - 1
src/app/directives/graphiteSegment.js

@@ -94,7 +94,7 @@ function (angular, app, _, $) {
           };
 
           $input.attr('data-provide', 'typeahead');
-          $input.typeahead({ source: $scope.source, minLength: 0, items: 100, updater: $scope.updater });
+          $input.typeahead({ source: $scope.source, minLength: 0, items: 10000, updater: $scope.updater });
 
           var typeahead = $input.data('typeahead');
           typeahead.lookup = function () {

+ 32 - 23
src/app/services/dashboard/dashboardViewStateSrv.js

@@ -14,9 +14,12 @@ function (angular, _, $) {
     // like fullscreen panel & edit
     function DashboardViewState($scope) {
       var self = this;
+      self.state = {};
+      self.panelScopes = [];
+      self.$scope = $scope;
 
       $scope.exitFullscreen = function() {
-        if (self.fullscreen) {
+        if (self.state.fullscreen) {
           self.update({ fullscreen: false });
         }
       };
@@ -28,42 +31,48 @@ function (angular, _, $) {
         }
       });
 
-      this.panelScopes = [];
-      this.$scope = $scope;
-
       this.update(this.getQueryStringState(), true);
     }
 
     DashboardViewState.prototype.needsSync = function(urlState) {
-      if (urlState.fullscreen !== this.fullscreen) { return true; }
-      if (urlState.edit !== this.edit) { return true; }
-      if (urlState.panelId !== this.panelId) { return true; }
-      return false;
+      return _.isEqual(this.state, urlState) === false;
     };
 
     DashboardViewState.prototype.getQueryStringState = function() {
       var queryParams = $location.search();
-      return {
+      var urlState = {
         panelId: parseInt(queryParams.panelId) || null,
         fullscreen: queryParams.fullscreen ? true : false,
-        edit: queryParams.edit ? true : false
+        edit: queryParams.edit ? true : false,
       };
+
+      _.each(queryParams, function(value, key) {
+        if (key.indexOf('var-') !== 0) { return; }
+        urlState[key] = value;
+      });
+
+      return urlState;
+    };
+
+    DashboardViewState.prototype.serializeToUrl = function() {
+      var urlState = _.clone(this.state);
+      urlState.fullscreen = this.state.fullscreen ? true : null,
+      urlState.edit = this.state.edit ? true : null;
+
+      return urlState;
     };
 
     DashboardViewState.prototype.update = function(state, skipUrlSync) {
-      _.extend(this, state);
+      _.extend(this.state, state);
+      this.fullscreen = this.state.fullscreen;
 
-      if (!this.fullscreen) {
-        this.panelId = null;
-        this.edit = false;
+      if (!this.state.fullscreen) {
+        this.state.panelId = null;
+        this.state.edit = false;
       }
 
       if (!skipUrlSync) {
-        $location.search({
-          fullscreen: this.fullscreen ? true : null,
-          panelId: this.panelId,
-          edit: this.edit ? true : null
-        });
+        $location.search(this.serializeToUrl());
       }
 
       this.syncState();
@@ -76,7 +85,7 @@ function (angular, _, $) {
         if (this.fullscreenPanel) {
           this.leaveFullscreen(false);
         }
-        var panelScope = this.getPanelScope(this.panelId);
+        var panelScope = this.getPanelScope(this.state.panelId);
         this.enterFullscreen(panelScope);
         return;
       }
@@ -118,8 +127,8 @@ function (angular, _, $) {
       var fullscreenHeight = Math.floor(docHeight * 0.7);
       this.oldTimeRange = panelScope.range;
 
-      panelScope.height = this.edit ? editHeight : fullscreenHeight;
-      panelScope.editMode = this.edit;
+      panelScope.height = this.state.edit ? editHeight : fullscreenHeight;
+      panelScope.editMode = this.state.edit;
       this.fullscreenPanel = panelScope;
 
       $(window).scrollTop(0);
@@ -135,7 +144,7 @@ function (angular, _, $) {
       var self = this;
       self.panelScopes.push(panelScope);
 
-      if (self.panelId === panelScope.panel.id) {
+      if (self.state.panelId === panelScope.panel.id) {
         self.enterFullscreen(panelScope);
       }
 

+ 6 - 9
src/app/services/templateSrv.js

@@ -7,7 +7,7 @@ function (angular, _) {
 
   var module = angular.module('grafana.services');
 
-  module.service('templateSrv', function($q, $routeParams) {
+  module.service('templateSrv', function() {
     var self = this;
 
     this._regex = /\$(\w+)|\[\[([\s\S]+?)\]\]/g;
@@ -19,17 +19,10 @@ function (angular, _) {
       this.updateTemplateData(true);
     };
 
-    this.updateTemplateData = function(initial) {
+    this.updateTemplateData = function() {
       var data = {};
 
       _.each(this.variables, function(variable) {
-        if (initial) {
-          var urlValue = $routeParams[ variable.name ];
-          if (urlValue) {
-            variable.current = { text: urlValue, value: urlValue };
-          }
-        }
-
         if (!variable.current || !variable.current.value) {
           return;
         }
@@ -50,6 +43,10 @@ function (angular, _) {
       return match && (self._templateData[match[1] || match[2]] !== void 0);
     };
 
+    this.containsVariable = function(str, variableName) {
+      return str.indexOf('$' + variableName) !== -1 || str.indexOf('[[' + variableName + ']]') !== -1;
+    };
+
     this.highlightVariablesAsHtml = function(str) {
       if (!str || !_.isString(str)) { return str; }
 

+ 15 - 9
src/app/services/templateValuesSrv.js

@@ -18,17 +18,24 @@ function (angular, _, kbn) {
       }
     });
 
-    this.init = function(dashboard) {
+    this.init = function(dashboard, viewstate) {
       this.variables = dashboard.templating.list;
+      this.viewstate = viewstate;
       templateSrv.init(this.variables);
 
       for (var i = 0; i < this.variables.length; i++) {
-        var param = this.variables[i];
-        if (param.refresh) {
-          this.updateOptions(param);
+        var variable = this.variables[i];
+        var urlValue = viewstate.state['var-' + variable.name];
+        if (urlValue !== void 0) {
+          var option = _.findWhere(variable.options, { text: urlValue });
+          option = option || { text: urlValue, value: urlValue };
+          this.setVariableValue(variable, option, true);
         }
-        else if (param.type === 'interval') {
-          this.updateAutoInterval(param);
+        else if (variable.refresh) {
+          this.updateOptions(variable);
+        }
+        else if (variable.type === 'interval') {
+          this.updateAutoInterval(variable);
         }
       }
     };
@@ -63,7 +70,7 @@ function (angular, _, kbn) {
         if (otherVariable === updatedVariable) {
           return;
         }
-        if (otherVariable.query.indexOf('[[' + updatedVariable.name + ']]') !== -1) {
+        if (templateSrv.containsVariable(otherVariable.query, updatedVariable.name)) {
           return self.updateOptions(otherVariable);
         }
       });
@@ -92,7 +99,6 @@ function (angular, _, kbn) {
       var datasource = datasourceSrv.get(variable.datasource);
       return datasource.metricFindQuery(variable.query)
         .then(function (results) {
-
           variable.options = self.metricNamesToVariableValues(variable, results);
 
           if (variable.includeAll) {
@@ -102,7 +108,7 @@ function (angular, _, kbn) {
           // if parameter has current value
           // if it exists in options array keep value
           if (variable.current) {
-            var currentExists = _.findWhere(variable.options, { value: variable.current.value });
+            var currentExists = _.findWhere(variable.options, { text: variable.current.text });
             if (currentExists) {
               return self.setVariableValue(variable, variable.current, true);
             }

+ 5 - 0
src/css/less/overrides.less

@@ -516,6 +516,11 @@ div.flot-text {
   }
 }
 
+// typeahead max height
+.typeahead {
+  max-height: 300px;
+  overflow-y: auto;
+}
 
 // Labels & Badges
 .label-tag {

+ 2 - 0
src/test/specs/dashboardViewStateSrv-specs.js

@@ -20,6 +20,7 @@ define([
         viewState.update(updateState);
         expect(location.search()).to.eql(updateState);
         expect(viewState.fullscreen).to.be(true);
+        expect(viewState.state.fullscreen).to.be(true);
       });
     });
 
@@ -29,6 +30,7 @@ define([
         viewState.update({fullscreen: false});
         expect(location.search()).to.eql({});
         expect(viewState.fullscreen).to.be(false);
+        expect(viewState.state.fullscreen).to.be(false);
       });
     });
 

+ 18 - 0
src/test/specs/templateSrv-specs.js

@@ -62,6 +62,24 @@ define([
 
     });
 
+    describe('when checking if a string contains a variable', function() {
+      beforeEach(function() {
+        _templateSrv.init([{ name: 'test', current: { value: 'muuuu' } }]);
+        _templateSrv.updateTemplateData();
+      });
+
+      it('should find it with $var syntax', function() {
+        var contains = _templateSrv.containsVariable('this.$test.filters', 'test');
+        expect(contains).to.be(true);
+      });
+
+      it('should find it with [[var]] syntax', function() {
+        var contains = _templateSrv.containsVariable('this.[[test]].filters', 'test');
+        expect(contains).to.be(true);
+      });
+
+    });
+
     describe('updateTemplateData with simple value', function() {
       beforeEach(function() {
         _templateSrv.init([{ name: 'test', current: { value: 'muuuu' } }]);

+ 2 - 28
src/test/specs/templateValuesSrv-specs.js

@@ -27,20 +27,6 @@ define([
       });
     });
 
-    describe.only('should init values', function() {
-      var variables = [
-        { name: 'test', current: { value: 'hej' }}
-      ];
-      var dashboard = { templating: { list: variables } };
-
-      beforeEach(function() {
-        ctx.service.init(dashboard);
-      });
-
-      it('should update options array', function() {
-      });
-    });
-
     function describeUpdateVariable(desc, fn) {
       describe(desc, function() {
         var scenario = {};
@@ -139,12 +125,12 @@ define([
     describeUpdateVariable('and existing value still exists in options', function(scenario) {
       scenario.setup(function() {
         scenario.variable = { type: 'query', query: 'apps.*', name: 'test' };
-        scenario.variable.current = { value: 'backend2'};
+        scenario.variable.current = { text: 'backend2'};
         scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
       });
 
       it('should keep variable value', function() {
-        expect(scenario.variable.current.value).to.be('backend2');
+        expect(scenario.variable.current.text).to.be('backend2');
       });
     });
 
@@ -196,18 +182,6 @@ define([
       });
     });
 
-    describeUpdateVariable('and existing value still exists in options', function(scenario) {
-      scenario.setup(function() {
-        scenario.variable = { type: 'query', query: 'apps.*', name: 'test' };
-        scenario.variable.current = { value: 'backend2'};
-        scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
-      });
-
-      it('should keep variable value', function() {
-        expect(scenario.variable.current.value).to.be('backend2');
-      });
-    });
-
     describeUpdateVariable('with include All glob syntax', function(scenario) {
       scenario.setup(function() {
         scenario.variable = { type: 'query', query: 'apps.*', name: 'test', includeAll: true, allFormat: 'glob' };