SharePoint 2013 Custom AJAX filters on List View Web Parts

Requirement:
Implement ‘exact match’ filter against the list view web part on text values and drop down values using AJAX for great user performance. An alternative to filtering in a fly out menu of the list view web part’s column values.ajaxfilter1
Filter by ‘Title’
ajaxfilter2

Filter by ‘Task Status’
ajaxfilter3

Technique:

I am essentially emulating the column based filters that are out-of-the-box functionality of the SharePoint 2013 List view web parts. I do this by tracking down how the filtering is executed in the SharePoint inplview.js code. Then wrap the filtering JavaScript logic through custom text boxes.

ajaxfilter4

You can see the URL query string parameters reflect the filtering
/SitePages/Tickets.aspx#InplviewHashdba27ef6-7bff-4c0f-8ec1-b3e3e18fb0b8=FilterField1=LinkTitle-FilterValue1=Printer broken

For large lists (i.e. > 5,000 items), keep in mind to index the columns you want to filter by. You can find the setting in the List Settings page and find a link ‘Indexed Columns’

<table style="" border="0">
    <tr>
        <td>Title:</td>
        <td><input type="text" id="title" onblur="TitleFilter(); return false;" /></td>
        <td>
            <button onclick="TitleFilter(); return false;">Filter by Title</button>
        </td>
    </tr>
    <tr>
        <td>
            Task Status:
        </td>
        <td>
            <input type="text" id="taskStatus" onblur="TaskStatusFilter(); return false;" />
        </td>
        <td>
            <button onclick="TaskStatusFilter(); return false;">Filter by Task Status</button>
        </td>
    </tr>
    <tr>
        <td></td>
        <td></td>
        <td>
            <button onclick="TitleAndTaskStatusFilter(); return false;">Filter by Title AND Task Status</button>
        </td>
    </tr>
</table>
 

<script language='javascript'>
       function MyRefreshPageToEx(lvTableID, url, bForceSubmit) {

        // Hardcode reference to list view <table> Id attribute
        var tblv = document.getElementById("{BE9A90D1-BF5D-401B-8A9B-C44CCCD14241}-{DBA27EF6-7BFF-4C0F-8EC1-B3E3E18FB0B8}");
        var clvp = CLVPFromCtx(tblv);

        if (clvp != null && clvp.ctx.IsClientRendering) {
            clvp.RefreshPaging(url);
            clvp.ctx.queryString = url;
            if ((typeof clvp.ctx.operationType == "undefined" || clvp.ctx.operationType == SPListOperationType.Default) && Boolean(clvp.ctx.ListData)) {
                var fromPage = clvp.ctx.ListData.FirstRow - 1;
                var toPage = Number(GetUrlKeyValue("PageFirstRow", false, url));

                if (!isNaN(fromPage) && !isNaN(toPage) && fromPage != toPage)
                    fromPage < toPage ? (clvp.ctx.operationType = SPListOperationType.PagingRight) : (clvp.ctx.operationType = SPListOperationType.PagingLeft);
            }
        }
        else {
            SubmitFormPost(url, bForceSubmit);
        }
    }

    function TitleFilter() {
        var title = $('#title').val()
        var url;
        if (title) {
            url = "?List={BE9A90D1-BF5D-401B-8A9B-C44CCCD14241}&View={DBA27EF6-7BFF-4C0F-8EC1-B3E3E18FB0B8}&FilterField1=LinkTitle&FilterValue1=" + title
        }
        inplview.MyRefreshPage = MyRefreshPageToEx;
        inplview.MyRefreshPage(null, url, null);
    }

    function TaskStatusFilter() {
        var taskStatus = $('#taskStatus').val()
        var url;
        if (taskStatus) {
            url = "?List={BE9A90D1-BF5D-401B-8A9B-C44CCCD14241}&View={1F1B1E55-992B-4A03-98B2-9E5D34916611}&FilterField1=TaskStatus&FilterValue1=" + taskStatus
        }
        inplview.MyRefreshPage = MyRefreshPageToEx;
        inplview.MyRefreshPage(null, url, null);
    }

    function TitleAndTaskStatusFilter() {
        var title = $('#title').val()
        var taskStatus = $('#taskStatus').val()

        // HARD Code: query string to reference List GUID and View ID
        var url = "?List={BE9A90D1-BF5D-401B-8A9B-C44CCCD14241}&View={DBA27EF6-7BFF-4C0F-8EC1-B3E3E18FB0B8}&ViewCount=1&IsXslView=TRUE&IsCSR=TRUE&";
        if (title && taskStatus) {
          url = url + "FilterField1=LinkTitle&FilterValue1=" + title + "&FilterField2=TaskStatus&FilterValue2=" + taskStatus
        } else if (title && !taskStatus) {
          url = url + "FilterField1=LinkTitle&FilterValue1=" + title
        } else if (!title && taskStatus) {
          url = url + "FilterField1=TaskStatus&FilterValue1=" + taskStatus
        }
        inplview.MyRefreshPage = MyRefreshPageToEx;
        inplview.MyRefreshPage(null, url, null);
    }
</script>
 

There is some hard coding in the custom JavaScript code making references to the list view web part such as the table Id, list GUID and view GUID.

Find the table element Id as follows.
ajaxfilter5

Limitations

  • Inplview.js filtering only supports exact match of values and not partial match
  • OR operand is not supported in the inpview.js from what I can see.
  • This custom approach is a bit of “hack”. Since it is client side, there is no impact to the SharePoint farm on the server side. Nevertheless, follow any development guidelines in your team. At the least, regression testing is recommended on SharePoint product patching and upgrades for any breaking changes.

The solution fit

I think the better solution fit is where the user already knows exactly what to filter on, but doesn’t want to scroll a largest list of values through the OOTB column filters every single time. For example, filtering by exact name or a small number.

14 thoughts on “SharePoint 2013 Custom AJAX filters on List View Web Parts

  1. Aaron Hamilton

    Great stuff! Question for you: I am using JSLink to build a custom URL so users can filter by managed metadata by simply clicking a term. My code works on page load, but after I do my first filter the javascript no longer loads. This happens because JS executes on page load, not when inplview.js is called.

    How can I attach my javascript function to inplview so it’ll execute after every list refresh?

  2. Hi,

    The OR command is also working, you just need to change the following:
    – FilterFields1 instead of FilterField1 and FilterValues1 instead of FilterValue1
    – The Values should be separated by ‘;#’
    – The values should be URL encoded

    Example (will display the items with ID 1 or 2):
    FilterFields1=ID&FilterValues1=2%3B%231

    Cheers,
    Attila

  3. Francis Trujillo Jr

    Some Jquery to get rid of the hardcoded GUIDs and IDs.

    // Assuming you have placed a WebPart for a Custom List on your page, then
    // there is a table rendered for the list. This is the name you originally
    // named your list. Not any rename in an edited web part in a span.
    //
    // CALL => $(“table[summary=’OSIRDetails’]”).getGUIDsForTable();
    //
    // RESULTS returned in hash
    // =============================================================
    // webPartDivID => MSOZoneCell_WebPartWPQ3
    // tableID => {E2AA3C69-B35C-4EA1-A995-FD7E055287D8}-{A16C050C-A384-405F-B717-E532008039A5}
    // ctxNum => 59
    // ctxNumObj => [object Object]
    // listGUID => {E2AA3C69-B35C-4EA1-A995-FD7E055287D8}
    // viewGUID => {A16C050C-A384-405F-B717-E532008039A5}
    // listGUIDenc => %7BE2AA3C69-B35C-4EA1-A995-FD7E055287D8%7D
    // viewGUIDenc => %7BA16C050C-A384-405F-B717-E532008039A5%7D

    $.fn.getGUIDsForTable = function() {
    var $thisTable = $(this); // See how to call above

    if($thisTable !== null) {
    var grid = false;
    var $webPartDiv = $thisTable.closest(“div[id^=’MSOZoneCell_WebPartWPQ’]”);
    var webPartDivID = $webPartDiv.attr(“id”);

    if($webPartDiv.find(“div[id^=’spgridcontainer’]”).length > 0){
    $webPartDiv = $webPartDiv.find(“.ms-listviewgrid”);
    webPartDivID = $webPartDiv.attr(“id”);
    grid = true;
    }

    $webPartDiv.find(“.ms-viewheadertr th”).each(function (i) {
    var $thisHeader = $(this);
    if(grid){
    $webPartDiv.data(“ctxNum”, $thisHeader.closest(“tbody”).find(“tr:nth-child(2)”).attr(“iid”).split(“,”)[0]);
    }
    else {
    $webPartDiv.data(“ctxNum”, $thisHeader.find(‘div:first’).attr(‘CtxNum’));
    }
    });

    var ctxNum = $webPartDiv.data(“ctxNum”);
    var results = {};

    if(ctxNum !== undefined) {
    results[‘webPartDivID’] = webPartDivID;
    results[‘tableID’] = $thisTable.attr(“id”);
    results[‘ctxNum’] = ctxNum;
    results[‘ctxNumObj’] = eval(“ctx” + ctxNum);
    results[‘listGUID’] = results[‘ctxNumObj’].listName;
    results[‘viewGUID’] = results[‘ctxNumObj’].view;
    results[‘listGUIDenc’] = encodeURIComponent(results[‘listGUID’]);
    results[‘viewGUIDenc’] = encodeURIComponent(results[‘viewGUID’]);
    }

    for(var key in results) {
    console.log(key + ” => ” + results[key]);
    }
    }

    return results;
    }; // getGUIDsForTable

  4. I can’t get it to work… I just get the message “Unable to get property ‘clvp’ of undefined or null reference”
    I figured out that it is the function “CLVPFromCtx(ctxParam)” in INPLVIEW.js, but I have no idea how to fix this

    1. Emre

      I had the same issue, but I solved it when I used the GUID with capital letters.

      I got to this solution when I copied and pasted the GUID from the source code of my site. And since the GUID in the script of Roy is also in capital letters it could work out for you too if your ID was in small letters. It just gave me the error when I used small letters in the ID.

    1. Not sure about 2 web parts. But probably apply the same approach with one web part to both point to the same custom JS functions to execute. It’s been so long since I’ve done any further work on this auto-refresh. Hope other reading this post and working on it an provide suggestions.

Leave a Reply