Dashboards & Visualizations

How to pass multiselect input values to a drilldown target?

aamirs291
Path Finder

Everyone,

Assume that I have the token of $ticket_id$ on Page 1. The input type for this token is Multiselect. The multiselect search string is ticket_id="value1" OR ticket_id="value2".... The multiselect is working fine and loading the correct data for all the panels on Page 1, but when I have to drill down to another page; value1 and value2 are not getting passed. Instead, the multiselect search string is passing i.e ticket_id="value1" OR ticket_id="value2".... I understand that the multiselect string is replacing the token $ticket_id$.

Now, the drilldown target also has multiselect inputs. Is there a way to pass more than one value to the drilldown target?

The code for passing the values is as attached below:

  <drilldown target="_blank">
      <lnk>
        <![CDATA[ ticket_review?form.severity=$row.severity$&form.ticket_id=$ticket_id$&form.time.earliest=$time.earliest$&form.time.latest=$time.latest$ ]]>
      </lnk>
    </drilldown>

Let me know.

1 Solution

jeffland
SplunkTrust
SplunkTrust

I've had the same problem. The target dashboard needs the token url-encoded like form.ticket_id=value1&form.ticked_id=value2 and so on. My solution was to re-build the url-encoded token value in js before doing the drilldown, see here for sample code (not tested):

require([
    "splunkjs/mvc",
    "splunkjs/mvc/utils",
    "splunkjs/mvc/simplexml/ready!"
],
function(mvc, utils) {
    var tokens = mvc.Components.get("default");
    var sourceMultiselect = splunkjs.mvc.Components.getInstance("multiselect");

    var clickEventGenerator = ... // where your click comes from, e.g. a button or your table
    clickEventGenerator.on("click", function(e) {
        e.preventDefault(); // Stop default drilldown behavior
        // Create token string from time tokens and multivalue inputs
        var tokenString = "";
        tokenString = "?form.time_tok.earliest=" + tokens.get("time_tok.earliest") + "&form.time_tok.latest=" + tokens.get("time_tok.latest"); // Use your other own static tokens here, e.g. severity
        tokenString += returnUrldValues(sourceMultiselect.val(), "form.targetMultiselectTokenName"); // Add dynamic (mutlivalue) tokens here

        // Handle click event regularly
        var url = "/en-GB/app/appName/targetDashboard" + tokenString;
        utils.redirect(url, false, "_blank");
    });

    // Returns concatenation of all entries in multiVals with prefixed targetTokenName
    function returnUrldValues (multiVals, targetTokenName) {
        var string = "";
        for (var i = 0; i < multiVals.length; i++) {
            string += "&" + targetTokenName + "=" + multiVals[i];
        }
        return string;
    }
});

Hope that gets you started.

View solution in original post

mwarman
Engager

As an alternative to the JavaScript route, you could instead use the report search to set a field, then reuse that field in drilldown link:

With a multiselect producing a search string like (sourcetype=foo OR sourcetype=bar):

<input type="multiselect" token="sourcetypes" depends="$dt.earliest$,$dt.latest$" searchWhenChanged="true">
  <label>Source Type</label>
  <choice value="*">Show All</choice>
  <default>Show All</default>
  <search>
    <query>index=_internal | stats count by sourcetype</query>
    <earliest>$dt.earliest$</earliest>
    <latest>$dt.latest$</latest>
  </search>
  <fieldForLabel>sourcetype</fieldForLabel>
  <fieldForValue>sourcetype</fieldForValue>
  <prefix>(</prefix>
  <suffix>)</suffix>
  <valuePrefix>sourcetype=</valuePrefix>
  <delimiter> OR </delimiter>
</input>

Then using the above in the report search as a part of the search as well as setting a field _querystring changing (sourcetype=foo OR sourcetype=bar) into form.sourcetypes=foo&form.sourcetypes=bar.

index="_internal" | search $sourcetypes$ | eval _querystring=replace(replace(ltrim(rtrim("$sourcetypes$",")"),"("),"sourcetype=","form.sourcetypes=")," OR ","&") | stats count by sourcetype, _querystring

Worth pointing out that you have to make sure the field _querystring is in the results and then use dashboard visualisation to hide it.

Then this can be used in the drilldown link (using |n to make sure it doesn't get escaped):

<link>
  <![CDATA[mutliselect-drilldown?form.dt.earliest=$earliest$&form.dt.latest=$latest$&$row._querystring|n$]]>
</link>

Working example:

<form>
  <label>Multiselect Drilldown</label>
  <fieldset submitButton="false">
    <input type="time" token="dt" searchWhenChanged="true">
      <label></label>
      <default>
        <earliest>@d</earliest>
        <latest>now</latest>
      </default>
    </input>
    <input type="multiselect" token="sourcetypes" depends="$dt.earliest$,$dt.latest$" searchWhenChanged="true">
      <label>Source Type</label>
      <choice value="*">Show All</choice>
      <default>Show All</default>
      <search>
        <query>index=_internal | stats count by sourcetype</query>
        <earliest>$dt.earliest$</earliest>
        <latest>$dt.latest$</latest>
      </search>
      <fieldForLabel>sourcetype</fieldForLabel>
      <fieldForValue>sourcetype</fieldForValue>
      <prefix>(</prefix>
      <suffix>)</suffix>
      <valuePrefix>sourcetype=</valuePrefix>
      <delimiter> OR </delimiter>
    </input>
  </fieldset>
  <row>
    <panel>
      <title>Source Types</title>
      <table>
        <search>
          <query>
            <![CDATA[index="_internal" | search $sourcetypes$ | eval _querystring=replace(replace(ltrim(rtrim("$sourcetypes$",")"),"("),"sourcetype=","form.sourcetypes=")," OR ","&") | stats count by sourcetype, _querystring]]>
          </query>
          <earliest>$dt.earliest$</earliest>
          <latest>$dt.latest$</latest>
        </search>
        <option name="count">10</option>
        <option name="dataOverlayMode">none</option>
        <option name="drilldown">row</option>
        <option name="rowNumbers">false</option>
        <option name="wrap">true</option>
        <fields>["sourcetype","count"]</fields>
        <drilldown>
          <link>
            <![CDATA[mutliselect-drilldown?form.dt.earliest=$earliest$&form.dt.latest=$latest$&$row._querystring|n$]]>
          </link>
        </drilldown>
      </table>
    </panel>
  </row>
</form>

Check the working project out here: https://github.com/mcwarman/splunk-multiselect-drilldown/.

aamirs291
Path Finder

Thank you for the alternative mwarman. I will look into implementing this as well when I get the chance to.

0 Karma

jeffland
SplunkTrust
SplunkTrust

I've had the same problem. The target dashboard needs the token url-encoded like form.ticket_id=value1&form.ticked_id=value2 and so on. My solution was to re-build the url-encoded token value in js before doing the drilldown, see here for sample code (not tested):

require([
    "splunkjs/mvc",
    "splunkjs/mvc/utils",
    "splunkjs/mvc/simplexml/ready!"
],
function(mvc, utils) {
    var tokens = mvc.Components.get("default");
    var sourceMultiselect = splunkjs.mvc.Components.getInstance("multiselect");

    var clickEventGenerator = ... // where your click comes from, e.g. a button or your table
    clickEventGenerator.on("click", function(e) {
        e.preventDefault(); // Stop default drilldown behavior
        // Create token string from time tokens and multivalue inputs
        var tokenString = "";
        tokenString = "?form.time_tok.earliest=" + tokens.get("time_tok.earliest") + "&form.time_tok.latest=" + tokens.get("time_tok.latest"); // Use your other own static tokens here, e.g. severity
        tokenString += returnUrldValues(sourceMultiselect.val(), "form.targetMultiselectTokenName"); // Add dynamic (mutlivalue) tokens here

        // Handle click event regularly
        var url = "/en-GB/app/appName/targetDashboard" + tokenString;
        utils.redirect(url, false, "_blank");
    });

    // Returns concatenation of all entries in multiVals with prefixed targetTokenName
    function returnUrldValues (multiVals, targetTokenName) {
        var string = "";
        for (var i = 0; i < multiVals.length; i++) {
            string += "&" + targetTokenName + "=" + multiVals[i];
        }
        return string;
    }
});

Hope that gets you started.

aamirs291
Path Finder

Jeffland,
I will be accepting this answer since it seems to be the only good solution to the many multiselect questions being asked on this forum.
I am not versed in .js so I don't know where I should be adding this code ? Should I add this to an existing .js file or should I modify the code ,save it and then place the file in the C:\Program Files\Splunk\share\splunk\search_mrsparkle\exposed\js\splunkjs\mvc directory ?
Let me know. Thank you.

0 Karma

jeffland
SplunkTrust
SplunkTrust

If you don't yet use custom js on your dashboard, you edit your Simple XML from

<form>
  <label>Title</label>
  <fieldset ...

to

<form script="myCustomJsFile.js">
  <label>Title</label>
  <fieldset ...

and place myCustomJsFile.js in C:\Program Files\Splunk\etc\apps&lt;yourApp>\appserver\static.

0 Karma

aamirs291
Path Finder

Ok so I did as you mentioned and the default drilldown is still working it isn't getting prevented. The old URL is still populating.

I have done the following changes. Are these correct ? I have marked the changes I did in bold.

require([
     "splunkjs/mvc",
     "splunkjs/mvc/utils",
     "splunkjs/mvc/simplexml/ready!"
 ],
 function(mvc, utils) {
     var tokens = mvc.Components.get("default");
     var sourceMultiselect = splunkjs.mvc.Components.getInstance("multiselect");

 var clickEventGenerator = **table** // where your click comes from, e.g. a button or your table
 clickEventGenerator.on("click", function(e) {
     e.preventDefault(); // Stop default drilldown behavior
     // Create token string from time tokens and multivalue inputs
     var tokenString = "";
     tokenString = "&form**.time.earliest**=" + tokens.get("**time.earliest**") + "&form.**time.latest**=" + tokens.get("**time.latest**"); // Use your other own static tokens here, e.g. severity
     **tokenString = "?form.severity=" + tokens.get("row.severity");**
     tokenString += returnUrldValues(sourceMultiselect.val(), "form.**ticket_id**"); // Add dynamic (mutlivalue) tokens here

     // Handle click event regularly
     var url = "**http://localhost:8001**/en-US/app/**ticket_analysis/ticket_review**" + tokenString;
     utils.redirect(url, false, "_blank");
 });

 // Returns concatenation of all entries in multiVals with prefixed targetTokenName

 function returnUrldValues (multiVals, targetTokenName) {
     var string = "";
     for (var i = 0; i < multiVals.length; i++) {
         string += "&" + **ticket_id**+ "=" + multiVals[i];
     }
     return string;
     }
 });

-- Also is for(var .. correct ?

0 Karma

jeffland
SplunkTrust
SplunkTrust

What is table in the line you're assigning it to the variable clickEventGenerator? It should be something like mvc.Components.get("tableId"), where tableId is the id you've given your table in Simple XML. Also, you're overwriting tokenString in the fourth and fifth line of the on click function.
What is wrong with for (var ...?

0 Karma

aamirs291
Path Finder

Jeffland,

I was busy in some work so I wasn't able to implement the code until today.
So I modified the code as you mentioned and the URL is still not updating with what I would like it to be. It is showing the default URL. I am trying to modify this for an app which has already been built. So is the URL hard coded ? I am just guessing.
Like I mentioned earlier I don't have much knowledge about coding in js. So I felt
for(var .. was incorrect since a few examples of .js I saw had only for( ..

Do you have any suggestions regarding this ? Thank you.

0 Karma

jeffland
SplunkTrust
SplunkTrust

for (var i... declares the variable i in the scope of the current function, using for (i... would make it global - general rule being to use the smallest necessary scope.
Are there any errors on your console? Also, I suspect the main question remains: what do you assign to the variable table?

0 Karma

aamirs291
Path Finder
var clickEventGenerator = mvc.Components.get("severity_table");

The above is the modification I did to the code. severity_table is a table on the first page through which the value for severity is found.
Eg. If I click on the first row of severity_table I should get the corresponding value of severity from the first row , click on second row I should get the corresponding value of severity from the second row.. and so on.
Any click on the severity_table should start the drilldown to the next page along with the corresponding values for severity and other values like the multiselect inputs. The multiselect inputs are coming from a different panel than the severity table. And in the next page the values of the first page are passed and populated to new panels.

0 Karma

jeffland
SplunkTrust
SplunkTrust

And you gave your table that id in Simple XML like this?

<row>
  <panel>
    <table id="severity_table">
      <search...

Because then I don't see any reason it shouldn't basically work. Now with your explanation I doubt you can use "row.severity" to get the row of your click just like you could if you were doing this in Simple XML. Try the following:

clickEventGenerator.on("click", function(e) {
    e.preventDefault();
    console.log(e);
});

This should only stop the regular drilldown, and leave the e element on your console. You can check it to see that it has a data attribute which has your click event values. Try that and see if there are errors on your console. When that all works, you should be able to use

...
tokenString = "?form.severity=" + e.data["click.value"];
...

to get the leftmost cell value for the clicked row.

0 Karma

aamirs291
Path Finder

1) Yes the id in Simple XML is severity_table.
2) I opened the Console window on the first page in Chrome and the following is error is what I receive.

http://localhost:8001/en-US/splunkd/__raw/servicesNS/admin/MyAppName/static/appLogo.png 
Failed to load resource: the server responded with a status of 404 (Not Found)

3)You mentioned that I would now be able to get the left most cell value.. But the left most cell doesnt have the value which I need. I might need to change the order of the table to get this working.

Mainly if modifying the URL works I could then focus on 3) and try to get that working as well. Thanks for following up jeffland.

0 Karma

jeffland
SplunkTrust
SplunkTrust

That error has nothing to do with the problem, unfortunately.
If it's not the leftmost cell, then you should still be able to get the required row from the e object. It has all rows from your table, you access them via e.data["row.severity"] assuming your row is named severity (case sensitive of course).
You should set a breakpoint on the e.preventDefault(); row and have a look at the object.

0 Karma

aamirs291
Path Finder

jeffland,

Apologize for the delay in response. I got sometime to implement this today. I was able to have a look at the "e" object. The error I am facing in the console is as follows :

Uncaught TypeError: Cannot read property 'val' of undefined
    at constructor.eval (eval at globalEval (common.js:1), <anonymous>:21:59)
    at triggerEvents (common.js:205)
    at constructor.trigger (common.js:205)
    at triggerEvents (common.js:205)
    at child.trigger (common.js:205)
    at triggerEvents (common.js:205)
    at child.trigger (common.js:205)
    at eventsApi (common.js:205)
    at child.trigger (common.js:205)
    at child._emitDrilldownEvent (common.js:317)

The console is also highlighting this line of code :

        tokenString += returnUrldValues(sourceMultiselect.val(), "form.targetMultiselectTokenName");

It would be helpful if I received a response. Thank you.

0 Karma

jeffland
SplunkTrust
SplunkTrust

It looks like your code doesn't properly get your multiselect. Did you give it an id in Simple XML? If you didn't change it, it should be "multiselect" as per the line

var sourceMultiselect = splunkjs.mvc.Components.getInstance("multiselect");

Your Simple XML should declare the id like this:

...
<input id="multiselect" type="multiselect" token=...
0 Karma

aamirs291
Path Finder

That was it.. The input id wasn't set at all.. Thanks a lot jeffland, appreciate your effort as well as the followup.

There still seems to be one issue though.. The default drilldown is not getting prevented.. i.e there are two pages now.. one with the multiselect search string(default) and another one which is showing up due to the js code you provided.

0 Karma

jeffland
SplunkTrust
SplunkTrust

That should not be the case if you're using e.preventDefault(); in the function which you call on('click' of the table, i.e.

clickEventGenerator.on("click", function(e) {
    e.preventDefault(); // Stop default drilldown behavior
0 Karma

aamirs291
Path Finder

This finally resolved Jeffland. Thank you.

I was using the following for drilling down to another page using Simple XML :

   <lnk>
     <![CDATA[ ticket_review?form.severity=$row.severity$&form.ticket_id=$ticket_id$&form.time.earliest=$time.earliest$&form.time.latest=$time.latest$ ]]>
   </lnk>
 </drilldown>

So the above needed to be removed from the Simple XML and then the .js code started working. I think preventDefault means : Stop the drilldown to the search page; because clicking on the table without having the .js code would go to the search page. Adding the .js code would stop this.

Another answer from you helped in resolving this.

https://answers.splunk.com/answers/492942/table-drilldown-disable-link-conditionally.html

Get Updates on the Splunk Community!

What's new in Splunk Cloud Platform 9.1.2312?

Hi Splunky people! We are excited to share the newest updates in Splunk Cloud Platform 9.1.2312! Analysts can ...

What’s New in Splunk Security Essentials 3.8.0?

Splunk Security Essentials (SSE) is an app that can amplify the power of your existing Splunk Cloud Platform, ...

Let’s Get You Certified – Vegas-Style at .conf24

Are you ready to level up your Splunk game? Then, let’s get you certified live at .conf24 – our annual user ...