Content Search Webpart Dynamic Filtering

Wednesday, August 13, 2014

13

The Content Search webpart (CSWP) introduced in SharePoint 2013 is an extremely powerful and flexible tool for displaying dynamic content.  It can surface any content from the Search index, and display templates allow us complete control over how the results are rendered.

Each CSWP is associated with a search query which drives its content.  This post will focus on scenarios in which the search query itself needs to be dynamic.  The techniques in this post can be applied to the Search Results webpart as well.

There are several approaches to making the webpart's query dynamic:
  1. Use built-in query variable tokens
  2. Extend the CSWP with server side code
  3. Dynamically update the query with Javascript

Query Variables

The CSWP supports a set of predefined variables that can be used to build dynamic queries.  The variable tokens are replaced with actual values when the CSWP loads.  There's a plethora of variables to choose from--some of the more useful ones are Page Field, Site Property, User, URL Token, and Query String.

Say we have a page called Orders.aspx, with a CSWP that displays Orders.  We want to limit its results to orders for a specific Product, to be specified in the query string.  For instance, Orders.aspx?ProductID=3 should display only Orders with a ProductID of 3.

Assuming we have an Orders content type and a search managed property ProductID, this can be achieved by setting the CSWP's query to:

  contentType:Orders ProductID:{QueryString.ProductID}

Refer to this Technet article for the complete list of variable tokens.

Extending the CSWP

While query variables can handle many common scenarios, sometimes we need a little custom logic, or a field not available in the OOB variables.  What if we want to plug the current year into the query?  Or a value from another SP list?  Or from a user control on the page?

One solution is to extend the CSWP with server side code to intercept and update the query.  I am not going to go over the implementation details as there are already several good blog posts on it.

This article is a great tutorial on extending the CSWP to inject SharePoint Variation Labels into the query.

Dynamically Updating the CSWP with Javascript

If we can't use server side code, or need to dynamically change the results after the page loads, it is also possible to update the query using Javascript.  Note that some of the methods used in this approach are undocumented.  Use at your own risk!

Consider this scenario:  we have a CSWP that displays a list of Orders.  We want to query for orders related to a specific Product, determined by a dropdown list on the page.  When the dropdown selection is changed, the CSWP's results should dynamically update (without reloading the page).

First, add a CSWP to the page and set the default query that should run when the page loads.  In this case, I filter for Orders with Product ID 1.


Next, we will create a drop down box with Product options, and Javascript to update the CSWP on change.  I assume that jQuery is available on the page.  For prototyping purposes, we can just drop the markup in a Content Editor webpart.

To create the dropdown:

<select id="Products">
   <option value="1" selected="selected">Plushies</option>
   <option value="2">Computers</option>
   <option value="3">Unspeakable Items</option>
</select>

And now the good stuff... we attach a change handler to kick off some code when the dropdown is updated.  In the handler, we iterate through all the active data providers, looking for the ones associated with CSWPs.  In this example there would only be one.

Then, update the data provider's query to filter on the selected Product ID.  Finally, we call issueQuery() to execute.  The data provider's associated CSWP should automatically pick up the new results and update its display.

<script type="text/javascript">
  $(document).ready(function () {
    // Attach change event handler to Products dropdown
    $('#Products').change(function () {

      // Get selected product ID
      var prd = $(this).val();

      // Loop through Query Groups
      var groups = Srch.ScriptApplicationManager.get_current().queryGroups;

      $.each(groups, function () {
        // Look for query groups associated with a CSWP
        if (this.displays != null && this.displays.length > 0) {
          if (this.displays[0] instanceof Srch.ContentBySearch) {
            // Update and execute query
            var newQuery = 'contentType:Orders productID:' + prd;

            this.dataProvider.set_queryTemplate(newQuery);
            this.dataProvider.issueQuery();
          }
        }
      });
    });
  });
<script type="text/javascript">

 

Addendum: Targeting a Specific CSWP


I've been asked how to change the query of a specific CSWP, if there are more than one on the page?  In the example above where we search through the query groups, it's impossible to tell which is which.

We can leverage the $getClientControl function for this.  This approach requires using a custom display template.  $getClientControl takes as input any HTML element within a CSWP, and returns the containing CSWP object.

Now, we still have no way to grab an HTML element from inside that specific CSWP.  But if we're using a custom display template, we can tag an element with a custom ID or css class.

For example, give a DIV in the display template a special ID:

<div id="MyCSWP">Some content</div>

Now we can grab the CSWP object with $getClientControl( $("#MyCSWP")[0] ):

var ctrl = $getClientControl( $("#MyCSWP")[0] );

ctrl.get_dataProvider().set_queryTemplate(newQuery);
ctrl.get_dataProvider().issueQuery();

Note that $getClientControl expects an HTML element, not a jQuery object. So, passing $("#MyCSWP") will not work.  It has to be $("#MyCSWP")[0].  Alternatively, use document.getElementById.