суббота, 1 сентября 2012 г.

Search in XsltListViewWebPart


I had the following problem in our project: my customer has a long list with many text fields. I should give his users a tool for quick navigation in this list, as well as for searching  and editing elements. The best solution was a text filter. When a user enters a text into it, the list automatically is filtered by all columns as follows:
First I added XsltListViewWebPart (XLVWP) with a default view, then I added an input text box with a 'Search' button:
<input type="text" name="searchText" />
<button type="submit">Search</button>

I configured a new ParameterBinding element in the XLVWP to bind it with my text box:
<ParameterBinding Name="SearchText" Location="Form(searchText)" DefaultValue="" />

Into View parameter I have added following query:
<Query>
  <Where>
    <Or>
      <Or>
        <Contains>
          <FieldRef Name="Title"/>
          <Value Type="Text">{SearchText}</Value>
        </Contains>
        <Contains>
          <FieldRef Name="Author"/>
          <Value Type="Text">{SearchText}</Value>
        </Contains>
      </Or>
      <Contains>
        <FieldRef Name="PostCategory"/>
        <Value Type="Text">{SearchText}</Value>
      </Contains>
    </Or>
  </Where>
  <OrderBy>
    <FieldRef Name="PublishedDate" Ascending="FALSE"/>
  </OrderBy>
</Query>

Now that I enter a text into my filter text box and press  'Search' button my list is filtered by Title, Author and Category columns. I can see here 3 important problems:
1.       the user has to press  'Search' button to start filtering instead of simply entering the text
2.       The user has to wait for page reload
3.        When the user first opens this page the list is empty because the filter is empty.
I started fixing these problems one by one. First I added asynchronous update to my list view. Check 'Enable Asynchronous Update' and 'Show Manual Refresh Button' in the properties of XLVWP :

 

Users have got a manual refresh button in the right-hand upper corner of the list:

When they enter a text into the filter text box and press this button, XLVWP is filtered without the page reload.  I found an event receiver in IE developer tools:

javascript: __doPostBack('ctl00$m$g_09891d16_ead7_4eb6_9588_3c2eb636c6eactl02','cancel');return false;

I added it to the onkeyup event handler of my filter text box and then removed  'Search' button:
Search: <input onkeyup="javascript: __doPostBack('ctl00$m$g_09891d16_ead7_4eb6_9588_3c2eb636c6ea$ctl02','cancel');" />

Great, now the list is filtered without page update while the user inputs the text. Ok, but there remains the last problem: an empty list when the user first comes to the page. To solve it I used a calculated field in my list: _TitleToFilter with formula: ="###"&Title. Then I added a default value to the binding parameter: ###
<ParameterBinding Name="SearchText" Location="Form(searchText)" DefaultValue="###" />

In the query I replaced Title column with _TitleToFilter:
<Contains>
  <FieldRef Name="_TitleToFilter"/>
  <Value Type="Text">{SearchText}</Value>
</Contains>

Now that the filter is empty, the sequence of three sharps (###) is used as a filter pattern. And all items have this substring in their _TitleToFilter column.

Ok, but a new problem occured: when the user clears the filter text box, the list becomes empty. The default value does not apply because the filter sends a postback parameter but with an empty value. So I added a new hidden field to send the filter value to my XLVWP and fill this field with javascript while the user enters the text into the filter:

<input type="hidden" name="searchText" id="searchText" />
Search: 
<input onkeyup="document.getElementById('searchText').value = this.value == '' ? '###' : this.value; javascript: __doPostBack('ctl00$m$g_09891d16_ead7_4eb6_9588_3c2eb636c6ea$ctl02','cancel');" />

Now it works perfectly. There is no need for the manual refresh button now. To remove it form XLVWP you can just uncheck 'Show Manual Refresh Button' in its properties.