Local datasource request

Can we make a local datasource request rather than querying the server.

I am using “get-options” in “metric-segment” directive. The function associated with get-options is making a datasource request to “this.backendSrv.datasourceRequest(options”). So rather than querying our datasource server is there any workaround to create a local request. For example, I want to return a list (array) of static values to “get-options”

You can do what you want in the get-options callback, are you developing a custom plugin? The metric segment is a generic UI directive, nothing to do with backendSrv datasourceRequest

Yes, I’m developing a custom plugin. get-options is expecting the data in the format returned by backendSrv datasourceRequest, right? I assumed backendSrv datasourceRequest is returning the data in the form of key-value pair and I tried to replicate the same but couldn’t get the data populated in metric-segment text box

get-options expects the data in form of an array of ui segments. A segment expects a string which is both the value and the key. Here is an example from code I am writing right now (in TypeScript):

getOptions() {
    return Promise.resolve(this.folders.map(folder => {
      return this.uiSegmentSrv.newSegment(folder.title);
    }));
  }

If you have key value data then you will have to:

  • hook into the on-change handler
  • get the chosen value
  • and then match it with your key value data to get the key.

In this case, I have a list of folders that have a title and an id field. The metric-segment uses the title value and then I have to manually find the folder id by filtering the list of folders by title. this.selectedFolderSegment is the segment that is bound to the segment field in the metric-segment directive.

onChange() {
    const selected = _.find(this.folders, {title: this.selectedFolderSegment.value});
    if (selected) {
      //do something with selected.id
    }
  }

This option kind of seems working.

Also can we use get-options on tag. I tried using it on input tag and but it didn’t return the results as expected. Can we tweak something to get it work on tag?

On tag? I don’t understand.

opps in above comment it missed <input> tag because I didn’t select code version

Anyway my question is -

In query.editor.html, rather than using get-option on <metric-segment> can we use it on <input> tag

<input type="text" style = "width : 170px;" class="gf-form-input"ng-model="segment.right" get-options = "ctrl.foo()" >{{segment.right}}</input>
Or is there any way I can make it work?

Why do you want to use input? metric-segment is an Angular directive which includes an input field. I don’t think I understand what you are trying to do.

Here is my html for the example I had in my earlier answer:

<metric-segment segment="ctrl.selectedFolderSegment"
      get-options="ctrl.getOptions()"
      on-change="ctrl.onChange()"></metric-segment>

I know it works on <metric-segment>, I was just curious if I could make it work on <input>.

The reason to use <input> tag is I am trying to implement auto-suggestion. On each key stroke, I should be able to populate the field with matching suggested values. With metric-segment I can’t type in. Hence the use of input tag.

You can just use typeahead if you do not want the select/dropdown behavior that is included in the metric-segment directive.

HTML:

<input type="text" bs-typeahead="ctrl.searchUsers" ng-model="ctrl.userName">

Typescript code:

constructor(private $scope, private $http, private backendSrv) {

    this.usersSearchCache = [];
    this.searchUsers = (queryStr, callback) => {
      if (this.usersSearchCache.length > 0) {
        callback(_.map(this.usersSearchCache, u => u.login));
        return;
      }

      this.backendSrv.get('/api/users/search?perpage=10&page=1&query=' + queryStr).then(result => {
        this.usersSearchCache = result.users;
        callback(_.map(result.users, u => u.login));
      });
    };
  }

Alright!
Let me try this approach. Thanks for your input.

@daniellee, I am just curious to know how bs-typeahead works.
Does the function “ctrl.searchUsers” get called on each keystroke or there’s more to it?

That’s pretty much all it does. It is a slightly modified version of the old bootstrap 2 typeahead component: http://getbootstrap.com/2.3.2/javascript.html#typeahead

Here is the code for it: https://github.com/grafana/grafana/blob/master/public/vendor/bootstrap/bootstrap.js#L1036

You could use lodash debounce to make it a little bit smarter and not query for every keypress.

Perfect! This is what I was looking for.

Thank you! :slight_smile:

1 Like

@daniellee I see bs-typeahead is making continuous multiple requests to backend even on just page refresh. Is there a way to limit it to just one request say only on a keystroke?

It sounds strange that it gets triggered several times on page refresh. I don’t have the answer for that but you have the code so you can investigate or debug it yourself to find out why. I usually use metric-segment and not bs-typeahead directly.

To reduce the number of calls to the backend, use debounce (limit calls so that at least x milliseconds has passed since the last call). There is lodash debounce as mentioned before:

 this.debouncedSearchUsers = _.debounce(this.searchUsers, 500, {'leading': true, 'trailing': false});

and there is also angular debounce - an example from Grafana’s codebase.

Angular docs for ngModelOptions - https://docs.angularjs.org/api/ng/directive/ngModelOptions

I see where I was wrong in using bs-typeahead.

I was assigning a function call to bs-typeahead rather than function reference and hence it resulted in multiple requests to the backend.

<input type="text" bs-typeahead="ctrl.searchUsers()" ng-model="ctrl.userName">

Now in the code snippet, you provided, I was wondering how are you passing “queryStr” variable to the function “ctrl.searchUsers”? I meant to say how does the function receive value for the variable “queryStr”