[O365] SharePoint Search dropdown refiner template

In my previous O365 post, I showed how you can make use of jQuery and jQuery UI to make a search refiner filter template that supports autocomplete on the “other” textbox shown in a multivalued dropdown. With that idea in mind, I imagined it would be pretty easy to use the same concept to create a filter template that shows a dropdown list with possible filter values. And it turns out it is pretty simple, so here are the steps.

As with my previous post, I’m not going to outline all of the steps to create a filter display template, instead I’d like to refer to @eliostruyf‘s blog for that (see here). As you’ll find, the default multivalued dropdown template uses two files; Filter_MultiValue and Filter_MultiValue_Body. So as with the autocomplete example, we’ll make copies of these.

In this case, there is no real need to change the Filter_Multivalue file, except for loading a custom javascript file like this:

<script>
  $includeScript("","~sitecollection/Scripts/yourscriptfile.js");
</script>

The rendering of the body of the filter is done in the Filter_Multivalue_Body file. Normally you’ll see the values rendered like this:

taxonomyrefiner

What we’d like to do is change this list for a dropdown which is way more convenient for use with a larger number of choices. Remember though, you might want to consider the autocomplete one whenever your list is becoming too large.

To do so, we disable the rendering of unselected items. Leave the selected ones rendered so the user can see what selection he or she made. To do so, change the following lines (50- 86):

<!--#_ 
for (var i = 0; i < filters.length; i++){
    var filter = filters[i];
    if(!$isNull(filter)){
        var isSelected = Boolean(filter.IsSelected);
        var inputName = propertyName + '_ChkGroup';
        var inputId = inputName + "_" + filter.RefinementName;
        var nameClass = "ms-ref-name " + (showCounts ? "ms-displayInline" : "ms-displayInlineBlock ms-ref-ellipsis");
_#-->
        <div id="Value">
<!--#_
        if(isSelected) {
_#-->
            <input type="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" value="_#= $htmlEncode(filter.RefinementToken) =#_" checked="" />
<!--#_
        } else {
_#-->
            <input type="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" value="_#= $htmlEncode(filter.RefinementToken) =#_" />
<!--#_
        }
_#-->
            <label for="_#= $htmlEncode(inputId) =#_" class='_#= nameClass =#_'>
            _#= $htmlEncode(filter.RefinementName) =#_
<!--#_
          if (showCounts) {
_#-->
            <span id='RefinementCount' class='ms-ref-count ms-textSmall'> (_#= $htmlEncode(Srch.U.toFormattedNumber(filter.RefinementCount)) =#_) </span>
<!--#_
          }
_#-->
            </label>
        </div>
<!--#_
    }
}
_#-->

To:

<!--#_
    var allSelected = true;

    for (var i = 0; i < filters.length; i++){
        var filter = filters[i];
        if(!$isNull(filter)){
            var isSelected = Boolean(filter.IsSelected);
            allSelected = allSelected && isSelected;
            var inputName = propertyName + '_ChkGroup';
            var inputId = inputName + "_" + filter.RefinementName;
            var nameClass = "ms-ref-name " + (showCounts ? "ms-displayInline" : "ms-displayInlineBlock ms-ref-ellipsis");
_#-->
<!--#_
            if(isSelected) {
_#-->
<div id="Value">
    <input type="checkbox" class="ms-padding0 ms-margin0 ms-verticalAlignMiddle" id="_#= $htmlEncode(inputId) =#_" name="_#= $htmlEncode(inputName) =#_" data-displayValue="_#= $htmlEncode(filter.RefinementName) =#_" value="_#= $htmlEncode(filter.RefinementToken) =#_" checked="" />
    <label for="_#= $htmlEncode(inputId) =#_" class='_#= nameClass =#_'>
        _#= $htmlEncode(filter.RefinementName) =#_
    </label>
</div>

<!--#_      } _#-->
<!--#_
        }
    }
_#-->

Without any values selected (default) the template won’t render a thing any more. Note that I added a variable “allSelected” which tracks whether all the items rendered are selected items. If this is the case, the user already selected some items and I do not want the dropdown to render any more. Whether you also want to do this is totally up to you.

Next, we’ll add some code to render a pretty straightforwarde HTML <select> control with <option> items for each refiner value:

<!--#_ if(!allSelected) { _#-->

<div id="OtherValue" style="width: 260px;">
    Select the items you want to filter on and then click 'apply' to filter the results. Select 'clear' to clear the filters to reselect different values.
</div>
<select onchange="Custom.Jsom.Refiners.addDropdownInput($(this), this.value);" id="_#= $htmlEncode(propertyName) =#__select">
    <option value="">Select a value</option>

    <!--#_
        for (var i = 0; i < filters.length; i++){
            var filter = filters[i];
            if(!$isNull(filter)){
                var inputName = propertyName + '_ChkGroup';
                var inputId = inputName + "_" + filter.RefinementName;
                var nameClass = "ms-ref-name " + (showCounts ? "ms-displayInline" : "ms-displayInlineBlock ms-ref-ellipsis");
    _#-->
    <option value="_#= $htmlEncode(filter.RefinementName) =#_">_#= $htmlEncode(filter.RefinementName) =#_</option>
    <!--#_
            }
        }
    _#-->

</select>

<!--#_ } _#-->

And there you are, the refiner values are now rendered as a dropdown in SharePoint:

refiner_dropdownclosed

And with values:

refiner_dropdownopened

Ok, now obviously we’re not done yet as this is a multivalued refiner and by default a dropdown only allows you to select a single value. So I added a method which is called on the dropdowns “onchange” event. This method looks like this:

function addAutocompleteInput(input, item) {
    var container = input.parent().parent().parent();
    var name = item.label;


    var txtInputName = input.attr('name');
    var inputName = txtInputName.substring(0, txtInputName.indexOf('_')) + '_ChkGroup';
    var inputId = inputName + '_' + name;
    var inputValue = 'equals("' + name + '")';

    addInput(inputId, inputName, inputValue, name, container);
}

function addInput(inputId, inputName, inputValue, value, container) {
    var inputClass = "ms-padding0 ms-margin0 ms-verticalAlignMiddle";
    var nameClass = "ms-ref-name ms-displayInlineBlock ms-ref-ellipsis";

    var div = $('<div />', { id: 'Value' }).prependTo(container);
    $('<input />', { type: 'checkbox', id: inputId, name: inputName, 'data-displayValue': value, value: inputValue, checked: '', class: inputClass }).appendTo(div);
    $('<span>&#160;</span>').appendTo(div);
    $('<label />', { 'for': inputId, text: value, class: nameClass }).appendTo(div);
}

I split this into two methods because I re-used the addInput method for the autocomplete box, you don’t need to if you don’t want to. The code is pretty self-explanatory I think, basically it renders a <div id=”Value”> container, holding a <input> checkbox with a <label> for the text value. This is exactly how SharePoint would render it too, which you can see in the original filter template code. The name of the <select> is used to construct a name for the checkbox controls and the selected value and label of the refiner are used to put into the select box.

When the user now selects a value from the dropdown, it’s rendered just like a regular refiner value:

refiner_dropdownselected

As you can see, you can select multiple values now and hitting “Apply” will refine your search based on all selected values.

One last thing you will probably need to to is increase the number of refiner values SharePoint will return. By default this number is set to 10, which will limit the amount of refiner options (thus dropdown options) to just 10, in which case a dropdown isn’t really required. I haven’t found a limit for this number just yet, but with larger numbers a dropdown isn’t really the best control any more in my opinion. I would probably try to limit it to about 100 items. You can find this setting in the refiner settings of the refiner web part:

refiner_dropdown_number

And that’s it, next to our already awesome autocomplete refiner we now have a dropdown refiner as well. Enjoy!

Update: the above mentioned Elio Struyf also has a solution for building a dropdown refiner template. I did this one just for practice and didn’t search for existing solutions, but I definitely recommend you check it out and choose the one you think is best! http://www.eliostruyf.com/part-4-create-dropdown-search-refiner-control/

Update: I’ve uploaded the two HTML files as example how you can implement this. Download here:

, , ,

Related posts

Long Term Support… or not?

In my previous O365 post, I showed how you can make use of jQuery and jQuery UI to make a search refiner filter template that supports autocomplete on the "other" textbox shown in a multivalued dropdown. With that idea in mind, I imagined it would be pretty easy to use the same concept to create a filter template that shows a dropdown list with possible filter values. And it turns out it is pretty simple, so here are the steps.

[DevOps] Should you migrate onto YAML release pipelines?

In my previous O365 post, I showed how you can make use of jQuery and jQuery UI to make a search refiner filter template that supports autocomplete on the "other" textbox shown in a multivalued dropdown. With that idea in mind, I imagined it would be pretty easy to use the same concept to create a filter template that shows a dropdown list with possible filter values. And it turns out it is pretty simple, so here are the steps.

Latest posts

Long Term Support… or not?

In my previous O365 post, I showed how you can make use of jQuery and jQuery UI to make a search refiner filter template that supports autocomplete on the "other" textbox shown in a multivalued dropdown. With that idea in mind, I imagined it would be pretty easy to use the same concept to create a filter template that shows a dropdown list with possible filter values. And it turns out it is pretty simple, so here are the steps.

[DevOps] Should you migrate onto YAML release pipelines?

In my previous O365 post, I showed how you can make use of jQuery and jQuery UI to make a search refiner filter template that supports autocomplete on the "other" textbox shown in a multivalued dropdown. With that idea in mind, I imagined it would be pretty easy to use the same concept to create a filter template that shows a dropdown list with possible filter values. And it turns out it is pretty simple, so here are the steps.

24 comments

  • Hi Jasper,

    Could you tell me where to add the above html code in the Filter_MultiValue_Body.html file in order to get the multivalue selection dropdown refiner.

    On which line do I add the following code in NotePad++

    <!–#_ if(!allSelected) { _#–>

    <div id=”OtherValue” style=”width: 260px;”>
    Select the items you want to filter on and then click ‘apply’ to filter the results. Select ‘clear’ to clear the filters to reselect different values.
    </div>
    <select onchange=”Custom.Jsom.Refiners.addDropdownInput($(this), this.value);” id=”_#= $htmlEncode(propertyName) =#__select”>
    <option value=””>Select a value</option>

    <!–#_
    for (var i = 0; i < filters.length; i++){
    var filter = filters[i];
    if(!$isNull(filter)){
    var inputName = propertyName + ‘_ChkGroup’;
    var inputId = inputName + “_” + filter.RefinementName;
    var nameClass = “ms-ref-name ” + (showCounts ? “ms-displayInline” : “ms-displayInlineBlock ms-ref-ellipsis”);
    _#–>
    <option value=”_#= $htmlEncode(filter.RefinementName) =#_”>_#= $htmlEncode(filter.RefinementName) =#_</option>
    <!–#_
    }
    }
    _#–>

    </select>

    <!–#_ } _#–>

    After adding the above code. On which line do I add your following code in NotePad++

    function addAutocompleteInput(input, item) {
    var container = input.parent().parent().parent();
    var name = item.label;

    var txtInputName = input.attr(‘name’);
    var inputName = txtInputName.substring(0, txtInputName.indexOf(‘_’)) + ‘_ChkGroup’;
    var inputId = inputName + ‘_’ + name;
    var inputValue = ‘equals(“‘ + name + ‘”)’;

    addInput(inputId, inputName, inputValue, name, container);
    }

    function addInput(inputId, inputName, inputValue, value, container) {
    var inputClass = “ms-padding0 ms-margin0 ms-verticalAlignMiddle”;
    var nameClass = “ms-ref-name ms-displayInlineBlock ms-ref-ellipsis”;

    var div = $(‘<div />’, { id: ‘Value’ }).prependTo(container);
    $(‘<input />’, { type: ‘checkbox’, id: inputId, name: inputName, ‘data-displayValue’: value, value: inputValue, checked: ”, class: inputClass }).appendTo(div);
    $(‘<span> </span>’).appendTo(div);
    $(‘<label />’, { ‘for’: inputId, text: value, class: nameClass }).appendTo(div);
    }

    Many Thanks

    Could I also ask do I just need to add your above 3 pieces of code to the Filter_MultiValue_Body.html file and would the Display Template then work in the Refinement WebPart.

    Or do I need to do something else with the WebPart etc. Could you please provide some more detailed guidance on this as I am not sure how to do the above.

    I look forward to your reply real soon…

    Many Thanks
    Best Regards

    • Hi Hayden, I’ve updated the post with two sample files. Let me know whether that helps.

      • Hey Jasper – I’m seeing a “Filter is not defined” error trying to use your sample files. It looks like your code sample may be missing the function call when your control is changed? Here’s how it’s called:

        onchange=”Filter.Jsom.Refiners.onDropDownChange(this);”

        Could this be in the StandardizationWebActions.js file that you’re including but isn’t in the attached package?

        • Hi Patrick, yes that is probably the cause of that issue. Basically what you need to attach is a method which handles the change in selected value and then reissues the command to filter the results. You can use the ootb files to see how SharePoint does that, the code is the same.

  • Hi Jasper,
    Many thanks for your reply, please do excuse me if I am being naïve here…

    I have tried to upload your two html files into the Master Page Gallery in SharePoint Online 2013 but unfortunately it has come back with the below error…. Could you please provide further guidance on this or alternatively could you post the two html file with all the exact correct code, so that it can be just uploaded to the Master Page and the dropdown with multiple checkbox selection could work.

    Many Thanks Jasper, Look forward to your reply real soon. Thx

    One or more of the following resource files failed to load: •/sites/refinement/_catalogs/masterpage/display templates/filters/filter_multivalue_dropdown_body.js
    •/sites/refinement/scripts/jquery-ui.min.js
    •/sites/refinement/_layouts/15/sp.runtime.js
    •/sites/refinement/_layouts/15/sp.js
    •/sites/refinement/_layouts/15/sp.taxonomy.js
    •/sites/refinement/scripts/standardizationwebactions.js

  • Jasper, I am only interested in creating your template called: [O365] SharePoint Search dropdown refiner template and do NOT want to do the other one called [O365] Autocomplete on taxonomy refiners.

    Could you possibly send some simple step by step guidance how this can be achieved..please. as I am unsure how this can be done, Many Thanks Jasper

    • Hi Hayden,

      The blog post is pretty much a step by step. If you do not understand what’s going on there, I would suggest you take a look at Elio’s blog which I mentioned which has a pretty solid walkthrough of how to create your own display templates. I cannot share the exact code because that’s from a custom project, so this is the best I can do.

  • Okay Jasper that’s fine, really appreciate your replies here, but could you just at least tell me how I get the below files to load in the master page gallery etc ..for instance, sites/refinement/scripts/jquery-ui.min.js (I do not understand what this is)

    how do I do upload this in master page so that the Filter_MultiValue.html file can draw from it….
    Sorry for the inconvenience, if you do feel it is inappropriate for you to reply, that’s fine I respect that. Many Thanks for your assistance in advance. Best Regards…

    One or more of the following resource files failed to load: •/sites/refinement/_catalogs/masterpage/display templates/filters/filter_multivalue_dropdown_body.js
    •/sites/refinement/scripts/jquery-ui.min.js
    •/sites/refinement/_layouts/15/sp.runtime.js
    •/sites/refinement/_layouts/15/sp.js
    •/sites/refinement/_layouts/15/sp.taxonomy.js
    •/sites/refinement/scripts/standardizationwebactions.js

    • That’s exactly the stuff that Elio handles on his blog. Please understand that creating custom templates is not a matter of “just upload and select”, that’s only the case when you have ready made templates which these are not. On Elio’s blog you can also find a link to a shared repository of ready made templates, I believe he even has one with a dropdown if I’m not mistaken. For all other templates (like this blog), you’ll need at least some understanding of how to edit HTML / JavaScript and debugging. If you don’t have enough knowledge, find a developer who can help.

  • Okay Jasper thanks for reply, I have used Elio’s dropdown template and it works fine in my search centre in the refinement webpart.

    The reason I don’t wish to use Elio is because he only provides a dropdown refiner without checkboxes and you cannot multi select values in his dropdown refiner.

    The reason why I really like your post is because it provides a dropdown with checkbox with multiple selection, if I am right…I am unable to find anyone who may have done a post like yours above.

    However, although I very much like your post unfortunately I am unable to get to the finishing line with your post. I think it maybe to advance for me at this moment in time.

    However, I did manage to get the refiner in the refinement webpart as a dropdown with your post, and with the description showing on the top of the dropdown textbox and with Select displayed in the dropdown. but I was unable to get it to multi select with check boxes. As I did not know where to insert this piece of code:

    function addAutocompleteInput(input, item) {
    var container = input.parent().parent().parent();
    var name = item.label;

    var txtInputName = input.attr(‘name’);
    var inputName = txtInputName.substring(0, txtInputName.indexOf(‘_’)) + ‘_ChkGroup’;
    var inputId = inputName + ‘_’ + name;
    var inputValue = ‘equals(“‘ + name + ‘”)’;

    addInput(inputId, inputName, inputValue, name, container);
    }

    function addInput(inputId, inputName, inputValue, value, container) {
    var inputClass = “ms-padding0 ms-margin0 ms-verticalAlignMiddle”;
    var nameClass = “ms-ref-name ms-displayInlineBlock ms-ref-ellipsis”;

    var div = $(‘’, { id: ‘Value’ }).prependTo(container);
    $(‘’, { type: ‘checkbox’, id: inputId, name: inputName, ‘data-displayValue’: value, value: inputValue, checked: ”, class: inputClass }).appendTo(div);
    $(‘ ’).appendTo(div);
    $(‘’, { ‘for’: inputId, text: value, class: nameClass }).appendTo(div);
    }

    Many Thanks for your time Jasper and appreciate your replies….

    Regards

  • hi Hayden

    i am not able to downlaod the attached files. i have exactly the same requirements.

    can you please send me the files

    • What’s wrong with the download? I just checked it, seems to be fine.

      • i ma looking for then file standardizationwebactions.js

        thanks a lot for this great blog

        manish saini

      • hi Jasper

        below is my js file code. when I tried to debug it is throwing error “custom is undefined” .

        • Hello Manish,

          I suspect you probably do not have a “Custom” namespace defined in your project. If you’re not familiar with JS namespaces I would recommend first doing some getting started courses on JS. Also, please don’t post the code snippets here which I sent to you privately.

          You probably need something like:

          var Custom = window.Custom|| {};
          Custom.Jsom = Custom.Jsom || {};

          I really advise you to first start with some basic javascript courses.

          • thanks a lot for this reply

            you are really great

            thanks
            manish saini

          • hi Jasper

            sorry for bothering you again. but I tied everything you said and but the code is giving me error “object and mentor doesn’t support the property autocomplete”.(during debugging)

            I have specified bith the files jquery.min.js and jquery-ui.min.js. I ma totally lost now .

            can you please provide any input?

            thanks a lot

          • Hello Manish,

            If you require autocomplete; you need to have the jQuery autocomplete plugin loaded. I’m guessing you haven’t loaded the plugin so the .autocomplete method would then be undefined.

          • hi Jasper

            I included both the flles
            RegisterSod(‘jquery.min.js’, Srch.U.replaceUrlTokens(“https://gnwfin.sharepoint.com/sites/DealRoom_DEV/RE/SiteAssets/jquery.min.js”));
            RegisterSod(‘jquery-ui.min.js’, Srch.U.replaceUrlTokens(“https://gnwfin.sharepoint.com/sites/DealRoom_DEV/RE/SiteAssets/jquery-ui.min.js”));

            but still getting error . I checked in the IE debugger . there files are not getting loaded

          • Ok, so when the files are not being loaded: that’s your problem. You should go and try to find out why they’re not loaded, has nothing to do with that line of code which calls .autocomplete. Check out this blog by Elio for more background info on how to correctly load script files from display templates: http://www.eliostruyf.com/correctly-including-scripts-display-templates/. From your sample, the following seems to be missing: “This will register the scripts, but they are not yet loaded if you go to your page. To load them you will need to make use of the SP.SOD.executeFunc / EnsureScriptFunc function.”

          • hi Jasper,

            now there is no error in my code but it is not showing the suggestions . below is my code.
            it is not going into findterms method.
            can you please provide any input?

            var locale = 1033;
            var clientContext = new SP.ClientContext(“https://xxxxx”);

            var web = clientContext.get_web();
            var list = web.get_lists().getByTitle(‘xxxx’)

            //var camlQuery = new SP.CamlQuery.createAllItemsQuery();
            var camlQuery = new SP.CamlQuery();
            camlQuery.set_viewXml(”);
            var items = list.getItems(camlQuery);

            clientContext.load(items,”Include(‘xxxxx’)”);

            clientContext.executeQueryAsync(
            Function.createDelegate(this, function () { deferred.resolve(items); }),
            Function.createDelegate(this, function (sender, args) { deferred.reject(sender, args); })
            );

            return deferred.promise();
            }

          • hi Jasper,

            after correcting all the things and I am getting the resulltset but that resultset is not coming as suggestions in the textbox.

          • hi Jasper

            finally the usggestions are coming but there is one issue inyour code. once you apply the suggestion and then clear it next time the suggestion won’t come up.

            thanks
            manish

Leave a Comment

Leave a Reply

Your email address will not be published. Required fields are marked *