Hi Team,
I have multiple "Multiselect" Input on my Dashboard which has search query which populates the result for Multiselect along with a static option of All (*)
Now, i want that whenever anyone selects any one or Multiple values in Multiselect input, "All" should automatically gets removed. Currently the user has to remove "All" and if user doesn't remove "All" then the result is skewed.
The results is getting skewed because we are using OR clause.
For example
When only ALL is selected (default) below search query runs
index=abc | search xzy=* | stats count by xyz
When other values are also selected along with ALL
index=abc | search xzy=* OR xzy=hello OR xzy=hi OR xzy=how | stats count by xyz
Now in the above case since "All" is by default selected, and even person selects other name the results show for all users as xzy=* remains and user has to explicitly remove ALL from the multiselect.
What i am looking for is whenever any user selects anything from the multiselect input, All should automatically be removed/de-selected.
I followed the advice by @jeffland in Link and using the below JS i was able to achieve what i was looking for.
require([
'jquery',
'splunkjs/mvc',
'splunkjs/mvc/simplexml/ready!'
], function($, mvc){
$('#multi').on("change",function(){
var multi1 = mvc.Components.get("multi");
var tokens = mvc.Components.getInstance("default");
var mytoken = tokens.get("multi")
if (mytoken.length > 1 && mytoken.includes("All"))
{
// If "All" was selected before current (more specific) selection, remove it from list
if (indexOfAll == 0)
{
var temp = mytoken.split(" ")
var temp1 = temp[1]
multi1.val(temp1);
} else
{
// "All" was selected last, clear input and leave only "All" in it
multi1.val("All");
}
}
});
});
I was not able to get either of the above answers to work for me in Splunk 6.6.1, so I wrote my own using plain Javascript without the need for jQuery. This is also a generic solution that works for any number of multi-selects on the page.
require([
'splunkjs/mvc',
'splunkjs/mvc/simplexml/ready!'
], function(mvc) {
function setupMultiInput(instance_id) {
// Get multiselect
var multi = mvc.Components.get(instance_id);
// On change, check selection
multi.on("change", (selectedValues) => {
if (selectedValues.length > 1 && selectedValues.includes("*")) {
var indexOfAll = selectedValues.indexOf("*");
// If "ALL" was selected before current (more specific) selection, remove it from list
if (indexOfAll == 0) {
selectedValues.splice(indexOfAll, 1);
multi.val(selectedValues);
multi.render();
} else {
// "ALL" was selected last, clear input and leave only "ALL" in it
multi.val("*");
multi.render();
}
}
});
}
var all_multi_selects = document.getElementsByClassName("input-multiselect");
for (j = 0; j < all_multi_selects.length; j++) {
setupMultiInput(all_multi_selects[j].id);
}
});
The assumption is that the default value is "*". But you can easily modify the code if you use something different for the default value.
Then all you have to do is to include the script at the top of your dashboard code:
<form script="path/to/multiselect.js">
@phoenix_down @niketnilay this works on all the multiselect present in the dashboard but i need to make it work on the single mutliselect.
could you please help?
Is there any reason why you only want it to work on a single multiselect? Usually people want this to be the default behavior for all multiselects on the page.
In any case, you can replace this:
var all_multi_selects = document.getElementsByClassName("input-multiselect");
for (j = 0; j < all_multi_selects.length; j++) {
setupMultiInput(all_multi_selects[j].id);
}
With:
var this_one_multiselect = document.getElementById("my_multiselect_id");
setupMultiInput(this_one_multiselect);
Where your multiselect has the id of "my_multiselect_id".
@phoenix_down i added id to panel and replaced the code but now its not resetting the "All" selection from mutiselect input.
if i keep all to all multiselect to reset its not working on the other panels to add new values. thats is why i need to keep restricting it to single multiselect
Keep in mind that with the code snippet as shown, your "All" label must have a value of "*****", otherwise it will not work. Please check to make sure that is the case in your code.
@phoenix_down this is the modified code JS with ID name, which has value for "*" in the input but its not working
require([
'splunkjs/mvc',
'splunkjs/mvc/simplexml/ready!'
], function(mvc) {
function setupMultiInput(instance_id) {
// Get multiselect
var multi = mvc.Components.get(instance_id);
// On change, check selection
multi.on("change", (selectedValues) => {
if (selectedValues.length > 1 && selectedValues.includes("*")) {
var indexOfAll = selectedValues.indexOf("*");
// If "ALL" was selected before current (more specific) selection, remove it from list
if (indexOfAll == 0) {
selectedValues.splice(indexOfAll, 1);
multi.val(selectedValues);
multi.render();
} else {
// "ALL" was selected last, clear input and leave only "ALL" in it
multi.val("*");
multi.render();
}
}
});
}
var this_one_multiselect = document.getElementById("my_multiselect_id");
setupMultiInput(this_one_multiselect);
});
<input type="multiselect" token="AURA" id="my_multiselect_id" searchWhenChanged="false">
<label>Select value</label>
<fieldForLabel>AURANAME</fieldForLabel>
<fieldForValue>AURANAME</fieldForValue>
<search>
<query>| inputlookup </query>
</search>
<choice value="*">All</choice>
<valuePrefix>"</valuePrefix>
<valueSuffix>"</valueSuffix>
<delimiter>,</delimiter>
</input>
If you use inspect elements in your browser's developer tools (in Chrome, it's View ->Developer->Inspect Elements) and select your multi-select, it should look something like this:
<div id="my_multiselect_id" data-cid="view14086" class="input input-multiselect" data-view="views/dashboard/form/Input" data-render-time="0.01">
Are you seeing that, or something else? What version of Splunk are you using?
@phoenix_down I am getting below things in inspect for the multiselect. i am using 8.0.1 version
<div class="splunk-view splunk-multidropdown splunk-choice-input" id="my_multiselect_id_10342" data-view="splunkjs/mvc/multidropdownview">
I see what the problem is now. The downside of custom JS code is that Splunk could change the structure of its elements under the hood and break your code, which is what happened here. Looks like Splunk 8.x changed some things under the hood. Not only is the "input-multiselect" class gone (which broke my original code that worked for any number of multi-selects), the id that you specified is now appended with a number (which breaks the new single multi-select code).
Try this code and see if it works:
require([
'splunkjs/mvc',
'splunkjs/mvc/simplexml/ready!'
], function(mvc) {
function setupMultiInput(instance_id) {
// Get multiselect
var multi = mvc.Components.get(instance_id);
// On change, check selection
multi.on("change", (selectedValues) => {
if (selectedValues.length > 1 && selectedValues.includes("*")) {
var indexOfAll = selectedValues.indexOf("*");
// If "ALL" was selected before current (more specific) selection, remove it from list
if (indexOfAll == 0) {
selectedValues.splice(indexOfAll, 1);
multi.val(selectedValues);
multi.render();
} else {
// "ALL" was selected last, clear input and leave only "ALL" in it
multi.val("*");
multi.render();
}
}
});
}
var all_multi_selects = document.getElementsByClassName("splunk-multidropdown");
for (j = 0; j < all_multi_selects.length; j++) {
setupMultiInput(all_multi_selects[j].id);
}
});
Hello! I am on Splunk 8.1.3 and I am attempting to use this JavaScript code to remove ALL from my multiselect. While on the component side, it does exactly as advertised and removes ALL from the multiselect component when something else is selected, Splunk itself does not appear to be honoring the update to the token. I.e., if my multi-select for a token called TOKEN_NAME starts as ALL then changes to item1, item2, the multi-select component works beautifully. Additionally, form.TOKEN_NAME in the URL shows the correct values, as I expected. The problem is, the part of my dashboard that uses the token never seems to register that the token was updated and still uses the originally ALL value.
I can't post my exact dashboard, but it is something similar to:
<form script="removeALLfromMultiselect.js">
<label>Example</label>
<fieldset submitButton="false" autoRun="true"/>
<input type="time" token="timeRange">
<label>Time Range</label>
<default>
<earliest>-12h@now</earliest>
<latest>now</latest>
</default>
</input>
<input type="multiselect" token="TOKEN_NAME" searchWhenChanged="true">
<label>Filter</label>
<fieldForLabel>item</fieldForLabel>
<fieldForValue>item</fieldForValue>
<search>
<query>sourcetype=SOURCE item | dedup item</query>
<earliest>-24h@now</earliest>
<latest>now</latest>
</search>
<choice value="*">ALL</choice>
<default>*</default>
<initialValue>*</initialValue>
<allowCustomValues>true</allowCustomValues>
<valuePrefix>item="*</valuePrefix>
<valueSuffix>*"</valueSuffix>
<delimiter> OR </delimiter>
</input>
</fieldeset>
<row>
<panel>
<title>Graph</title>
<chart>
<search>
<query>sourcetype=SOURCE $TOKEN_NAME$ ... (some more query stuff)</query>
<earliest>$timeRange.earliest$</earliest>
<latest>$timeRange.latest$</latest>
<sampleRatio>1</sampleRatio>
</search>
<option name="charting.axisTitleX.visibility">collapsed</option>
<option name="charting.axisTitleY.visibility">collapsed</option>
<option name="charting.axisTitleY2.visibility">collapsed</option>
<option name="charting.chart">line</option>
<option name="charting.chart.nullValueMode">zero</option>
<option name="charting.drilldown">all</option>
<option name="charting.layout.splitSeries">0</option>
<option name="charting.legend.mode">standard</option>
<option name="charting.legend.placement">none</option>
<option name="charting.height">862</option>
<option name="refresh.display">progressbar</option>
<option name="trellis.enabled">true</option>
<option name="trellis.scales.shared">0</option>
<option name="trellis.size">medium</option>
</chart>
</panel>
</row>
</form>
If I update the multiselect, the multiselect changes, the URL changes, the dashboard refreshes, but the chart does not use the right query. If I click the Open in Search button below the chart component, I can see the Search it is using is the wrong value for $TOKEN_NAME$, it is still using 'item="***"' for ALL.
Is there something here I am missing? I'd appreciate any help anyone can provide.
@phoenix_down its works on the multiselect to remove other values if i select "All" and vice versa.
i have option in multiselect 1 to unset token on multiselect2 if any value selection is reset on multiselect1 but now after token unset , i am not able to select /pick any values on multiselect2.
if i remove unset token tag its working fine, is any changes needed on JS to work?
`
<label>Select </label>
<fieldForLabel>SiteName</fieldForLabel>
<fieldForValue>SiteName</fieldForValue>
<search>
<query>| inputlookup </query>
</search>
<change>
<condition>
<unset token="form.Multiselct2"></unset></condition>
</change>
<choice value="*">All</choice>
<valuePrefix>"</valuePrefix>
<valueSuffix>"</valueSuffix>
<delimiter>,</delimiter>
</input>`
Instead of unsetting Multiselect2, can you set it to a default value?
@phoenix_down in mutiselect2 ALL values is not a * value. collectively of selection on multiselect1 so we cant set default value.
multiselect2 :
<div data-test-values="[]" data-test="multiselect" data-component="splunk-core:/splunkjs/mvc/components/MultiDropdown" data-size="medium" data-test-popover-id="popover-fa6e2cf8-a4b2-4561-b0a1-876edb52852e" data-popoveropen="false" role="listbox" class="NormalStyles__StyledBox-sc-2uz0a2-0 jYRcTO BoxStyles__Styled-sc-1f3k58w-0 eLYusu" data-inline="true" data-flex="true" style="width: 200px;"><div role="combobox" aria-haspopup="true" aria-expanded="false" class="NormalStyles__StyledInputWrapper-sc-2uz0a2-1 kodZxK"><input data-test="textbox" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-autocomplete="list" placeholder="Select..." data-size="medium" class="NormalStyles__StyledInput-sc-2uz0a2-2 eKdXWT" value="" style="flex-basis: 0em; width: 0em;"></div></div>
multiselect2 textbox:
<input data-test="textbox" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" aria-autocomplete="list" placeholder="Select..." data-size="medium" class="NormalStyles__StyledInput-sc-2uz0a2-2 eKdXWT" value="" style="flex-basis: 0em; width: 0em;" aria-activedescendant="active-item-6b2ed47c-f941-490b-8691-8cf1cdfe6c1c" aria-controls="popover-fa6e2cf8-a4b2-4561-b0a1-876edb52852e">
I missed your reply somehow. Can you post your code for "Multiselect2"?
@phoenix_down
below are the multiselect2.
multiselect1:
<input type="multiselect" id="ALL_RESET" token="shops" searchWhenChanged="true">
<label>Select shops</label>
<fieldForLabel>SiteName</fieldForLabel>
<fieldForValue>SiteName</fieldForValue>
<search>
<query>| inputlookup </query>
</search>
<choice value="*">---All shops---</choice>
<valuePrefix>"</valuePrefix>
<valueSuffix>"</valueSuffix>
<delimiter>,</delimiter>
<change>
<unset>form.multiselec2</unset>
</change>
</input>
multiselect2:
<input type="multiselect" token="Name" id="multiselectId" searchWhenChanged="false">
<label>Select Server(s)</label>
<search>
| inputlookup lookupsheet.csv| search Name IN ($Name$) | dedup Servers | table Servers | sort Servers | append[| inputlookup lookupsheet.csv| | search Name IN ($NAME$) | dedup Servers | table Servers | eval host="\"".Servers."\"" | stats delim="," values(host) AS host | mvcombine host] | eval host=if(host!="*",host,Servers) | fillnull value="ALL Servers" | sort Servers
<fieldForLabel>Servers</fieldForLabel>
<fieldForValue>host</fieldForValue>
<delimiter>,</delimiter>
</input>
multiselect 2 has dynamic value "All shops" which is generated from search.
so its not picking the check
ex: if selection is 2 shops, the SPl result will be
1.shop1
2, shp2
3.All shops(shop1,shop2)
its not working if results where dynamic coming from the search
Javascript for multiselect2"
require([
'underscore',
'splunkjs/mvc',
'splunkjs/mvc/simplexml/ready!'], function (_, mvc) {
var multiselect = splunkjs.mvc.Components.getInstance("multiselectId");
multiselect.on("change", function (value) {
var defaulLabel="All Shops";
if (value.length > 1 && ~(value.indexOf(defaulLabel))) {
if (value.indexOf(defaulLabel) == 0) {
value.splice(value.indexOf(defaulLabel), 1);
this.val(value);
console.log("Final ",value);
} else {
this.val(defaulLabel);
}
this.render();
}
})
});
yes it worked for me also...... Thanks for your code....@phoenix_down
Thanks @phoenix-down .it is working for all multi select inputs.
@tonyca, do up-vote this answer if it has helped 🙂
Just thought I'd post an alternative way of doing this. I'm not a big fan of adding javascript to my dashboards, so I've found a way to do this by adjusting the token for the input using the change & eval attributes. See the example below from one of our dashboards:
<input type="multiselect" token="host" searchWhenChanged="true">
<label>Hosts</label>
<search base="lookup">
<query>search $env$ | dedup host</query>
</search>
<valuePrefix>host=</valuePrefix>
<delimiter> OR </delimiter>
<default>*</default>
<choice value="*">All</choice>
<fieldForLabel>host</fieldForLabel>
<fieldForValue>host</fieldForValue>
<change>
<eval token="form.host">if(mvcount('form.host')=0,"*",if(mvcount('form.host')!=1,mvfilter('form.host'!="*"),'form.host'))</eval>
</change>
</input>
This will reset the token to 'All' if no options are selected, and will remove the 'All' option if more than 1 are selected.
UPDATE:
One of my colleagues found this didn't work properly. When you select a couple of hosts, the only way to get back to 'All' is to remove all your previous selections, at which point 'All' will re-appear. Below is the fix he added, this is the commentary from our internal Jira system:
Basically, because of the '*', the regex doesn't quite work, so we need to change the
<choice value="*">All</choice>
to
<choice value="All">All</choice>
so the regex mvfind works as expected. Then, set a second token dependent on the value of 'form.blah', which is then used in any post-process search [decided to change the name of the original multi-select form token, and then set a token with the old name, so it remains transparent to the post-process searches that use the original token].
So:
- it defaults to 'All' (All is alone at the start, index=0)
- if another env is chosen, 'All' is removed
- if 'All' is chosen, all other envs are removed (ie All is always alone at the start, index=0)
- if everything is removed, 'All' reappears (ie All is always alone at the start, index=0)
<input type="multiselect" token="env2" searchWhenChanged="true">
<label>Environment</label>
<search base="lookup">
<query>dedup environment</query>
</search>
<default>All</default>
<valuePrefix>environment=</valuePrefix>
<delimiter> OR </delimiter>
<choice value="All">All</choice>
<fieldForLabel>environment</fieldForLabel>
<fieldForValue>environment</fieldForValue>
<change>
<eval token="form.env2">case(mvcount('form.env2')=0,"All",mvcount('form.env2')>1 AND mvfind('form.env2',"All")>0,"All",mvcount('form.env2')>1 AND mvfind('form.env2',"All")=0,mvfilter('form.env2'!="All"),1==1,'form.env2')</eval>
<eval token="env">if(mvfind('form.env2',"All")=0,"environment=*",$env2$)</eval>
</change>
</input>
...still use $env$ where appropriate
<input type="dropdown" token="host" searchWhenChanged="true">
<label>Select a Host:</label>
<selectFirstChoice>true</selectFirstChoice>
<search base="lookup">
<query>search $env$ | dedup host</query>
</search>
Hope this helps, cheers.
@ashleyherbert following your elegant idea using change clause, I implemented mine below:
<form>
<label>Adaptive Option All</label>
<fieldset submitButton="false">
<input type="multiselect" token="dev_type" searchWhenChanged="true">
<label>Type of Sensor</label>
<choice value="*">All</choice>
<default>*</default>
<prefix>(</prefix>
<suffix>)</suffix>
<initialValue>*</initialValue>
<delimiter>, </delimiter>
<fieldForLabel>Device_Type</fieldForLabel>
<fieldForValue>Device_Type</fieldForValue>
<search>
<query>index="cse_scada"
| dedup Device_Type
| sort Device_Type<</query>
<earliest>0</earliest>
<latest></latest>
</search>
<change>
<eval token="form.dev_type">
case(
mvcount('form.dev_type')=0,"*", <!-- when there is non selected, set to "*" -->
mvcount('form.dev_type')>1 AND mvfind('form.dev_type',"*")>0,"*", <!-- when there are more than one selected, and "*" is not the first one, set to "*" -->
mvcount('form.dev_type')>1 AND mvfind('form.dev_type',"*")=0,mvfilter('form.dev_type'!="*"), <!-- when there are more than one selected, and "*" is the first one, remove "*" -->
1==1,'form.dev_type' <!-- otherwise no change -->
)
</eval>
</change>
</input>
</fieldset>
</form>
but it does not work.
When I selected a specific option "*" (All) does not disappear.
After having specific option(s), selecting "*" (All), the other option(s) do not disappear.
Would you please help to diagnose what could go wrong?
Thanks!
--
I suspect that it might be related how the list of values for the token dev_type is represented, as I observed, when only "*" is selected, the corresponding URL is
http://l-yshen:8000/en-US/app/search/trychangeadaptiveall?form.dev_type=*
when one more option is selected, say "UPS", the corresponding URL becomes
http://l-yshen:8000/en-US/app/search/trychangeadaptiveall?form.dev_type=*&form.dev_type=UPS
It seems the manipulation in the change clause takes no effect.