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.

SharePoint 2013 Custom List View Web Part Refresh

In the SharePoint 2013 list view web part, the paging, filtering and sorting functionality is driven by the inplview.js file. This is supported by AJAX calls against a RESTful service inplview.aspx. I am able to extend the JavaScript inplview object to implement client side refresh as data is updated on the server.
refresh1

To configure the automatic refresh by OOTB (out-of-the-box) configuration, go to the Web Part Properties: 

refresh2

 

What I find unattractive of this approach is that it is using the older AJAX UpdatePanel introduced in ASP .NET 2.0.

  • There is at least 23kb transferred for each refresh which is a relatively heavier in bandwidth.
    refresh3
  • The http response time is longer.
  • Using IE 8 browser, there is a memory leak such that memory utilization by the browser maxes out.
  • The out of the paging, filtering and sorting with the AJAX options do not work well together. For example, the AJAX refresh option sometimes shows no rows even after repeated combination of filtering and manual OOTB AJAX refresh. To resolve, refresh the page without any query string parameters.

The applicable business scenario or requirement is when frequently added and updated list items by multiple users are to be seen updated in the browser without user requiring to manually refresh the page. 

In SharePoint 2013, when the user interacts with the list view web part for activities such as paging, column filtering and sorting, there is an AJAX request making a RESTful call to inplview.aspx.
refresh4 Compared to the OOTB refresh, the inplview.aspx REST calls are significantly lighter in bandwidth and has faster response time.

How inplview.js Works

The javascript code that supports the list view web part paging, sorting and filtering is in the inplview.js file

The sequence of calls are made as follows:

  1. Browser
    1. User clicks on list view web part to page, sort and filter
    2. invoke objects in various SharePoint .js files
    3. invoke objects inplview.js
  2. Server
    1. inplview.aspx that is a RESTful http service
    2. HTTP response back to browser with only JSON formatted row data.

Using the chrome JavaScript debugger against inplview.js, I have identified the following sequence of function calls. To support the refresh, I have “forked” the code with my own custom functions.

Original OOTB sequence Customized “forked” sequence
RestoreAllClvpsNavigation Refresh_RestoreAllClvpsNavigation
EnumCLVPs(RestoreClvpNavigation) EnumCLVPs(Refresh_RestoreClvpNavigation)
RestoreClvpNavigation(clvp) <skip>
clvp.RestoreNavigation(); <skip>
CLVPRestoreNavigation() Refresh_RestoreClvpNavigation(clvp)

The Implementation

There is no explicit data refresh support in the inplview javascript code so I have injected custom code that extends the inplview javascript object to have supporting functions to implement refresh.

// automatic refresh based on interval
function autoRefresh()
{
 window.setInterval(listViewRefresh, 2000); // 20 seconds
}

// refresh all list view web parts on the page
function listViewRefresh() {
 $('#lblMessage').text('refreshed ').fadeIn("slow").fadeOut("slow"); // debugging
 inplview.MyRestoreAllClvpsNavigation = MyRestoreAllClvpsNavigation;
 inplview.MyRestoreAllClvpsNavigation();
}

// Enumerate list view web parts
function MyRestoreAllClvpsNavigation()
{
 EnumCLVPs(MyCLVPRestoreNavigation);
}

// refresh referencing list view web part
function MyCLVPRestoreNavigation(clvp) {
 var strHash = ajaxNavigate.getParam("InplviewHash" + clvp.WebPartId());
 if (strHash == null)
 strHash = '';

 var strInpl = '?' + DecodeHashAsQueryString(strHash);
 var strShowInGrid = GetUrlKeyValue("ShowInGrid", true, strInpl);

 if (strShowInGrid == "True") {
 InitGridFromView(clvp.ctx.view, true);
 }
 else if (clvp.ctx.inGridMode) {
 ExitGrid(clvp.ctx.view, true);
 }
 clvp.strHash = strHash;
 clvp.fRestore = true;
 var curRootFolder = GetRootFolder2(this);

 if (curRootFolder != null)
 strInpl = SetUrlKeyValue("RootFolder", unescapeProperly(curRootFolder), true, strInpl);
 clvp.RefreshPagingEx(strInpl, true, null);

 }

$(document).ready(function() {
 autoRefresh();
});

refresh5

Benefits

  1. Overcome IE8 browser memory leak issue
  2. Noticeable performance improvement over OOTB Ajax option
  3. The ability to extend and customize the client side rendering of the list view web part functionality

Caveats

  1. Upgrades and patches:
    Since this is extending from product JavaScript code, in the event of upgrades or patching, there could breaking changes.
    To mitigate this risk, remember to regression test after a product upgrade and patch.

Supportability

  1. Still supports general list view paging, column filtering and sorting.
  2. Exception: column filtering and sorting is not supported on lookup columns as it is already with OOTB.
  3. Ability to refresh all list view web parts that exist on the same page.

I’ll have a another blog post to add filtering based on user input by customizing the inpvliew.js

WorkflowManagerLogo

Workflow Manager 1.0 Setup and Validation on a Development Environment

Sharing my approach and knowledge of setting up a SharePoint 2013 development server with Workflow Manager 1.0. There are many step by step guides on installing and configuring Workflow Manager 1.0; however, this is from a topology point of view as well as areas of validating the setup.

Visual Studio 2012 SharePoint 2013 List and Content Type Designer: Hands-on Observations

After spending some time on developing a SP Hosted App, I would like to share my insights and observations with the new Office Developer tools for SharePoint 2013

Prerequisite: Web Platform Installer > Microsoft Office Developer Tools for Visual Studio 2012

Let’s walk through my observations from site columns, to content types and to list definitions.

Site Columns

  • There is no visual designer. Content types reference site columns and so having a site column designer would make much sense. It is unfortunate that one is absent.
  • Therefore, creating site columns is manual through the elements.xml file

VSContentTypeDesigner-1

Field Element Schema
http://msdn.microsoft.com/en-us/library/ms437580.aspx

Content Type

  • There is a new visual designerVSContentTypeDesigner-2VSContentTypeDesigner-3
  • Add existing OOTB site columns
  • Add existing custom site columns that were manually created.VSContentTypeDesigner-4
  • Remember, you cannot create custom site columns in the content type designer.
  • Limitation: Column ordering is manual through the elements.xml definition files. In other words, you cannot reorder site columns in the conten type designer.
  • Gotcha: Change in custom site column definition does not automatically reflect changes in content type. For example, changing site column name will break the referring content type.
    Therefore, you must remove the custom site column and then add updated site column.

List Definition and Instance

  • There is new visual designer
    Able to set List (template) name, url and description.
  • Content Types. Add existing OOTB content type or custom content type created in the VS projectVSContentTypeDesigner-5
  • Columns
    Add existing OOTB site columns or
    existing custom site columns in the VS project
    VSContentTypeDesigner-6Add list level columns manually
    VSContentTypeDesigner-7
  • Limitation: When creating a new custom column that are more complex in nature such as choice or person, there is no ability to configure the selectable choices. I would think you would have to revert to the xml files and fill in the rest of the xml manually. Having such a site column designer would have been very convenient.
  • When creating a list column, it automatically creates a list content type called “ListFieldsContentType” if one does not exist. This becomes the default. Any further site columns (OOTB or custom) added will be automatically added to this ListFieldsContentType content type.
    VSContentTypeDesigner-8
  • Note: Changes in content types referenced by the list definition are not automatically reflected. This is the same scenario mentioned above with site columns referenced by content types. So after you change a referenced content type, you must remove and then add back the updated content types to the list definition.
  • Set default content type so that the list’s ‘new’ button refers to the content type desired.
  • Limitation: List column ordering is manual through the xml definition files.
  • Not able to add predefined data rows in List Instance
    In ListInstance elements.xml, I added
    <DataRows>
    <Row>
    ….
    </Row>
    </DataRows>
    This resulted in a deployment error. Maybe I am missing something, but I was unsuccessful.
  • Views
    Able to specify which columns appear in the view and their order, row limit, read only, but nothing more than that. For example, no sorting ability, grouping, filtering, etc.
    VSContentTypeDesigner-9

After deploying to site collection:

VSContentTypeDesigner-10

Deployment Error: Failed to install app

Possible causes:

  • The xml schema files are malformed or have a syntax issue. Visual Studio can detect and highlight some of these in the Error pane.
  • A custom element definition is in conflict with an element already existing in the SharePoint farm.
    For example, deploying a list, content type or site column that has the same internal name as an existing artefact. Duplication error.

Troubleshooting:

The way I troubleshoot, is to remove snippets of xml definition and re-deploy to isolate trouble area.

I have looked in the 15 ULS Logs to find any detailed error messages and did not recognize anything helpful.

Conclusion:

The designers are a welcomed feature in Visual Studio 2012 for SharePoint developers. However, more can be achieved with site columns and handling changes to referenced elements. This is probably a large effort, since there are so many types of site columns with varying complexities such as ‘People and Groups’ and ‘Managed Metadata”. Nevertheless, it is one step better in getting through the xml hell.

Appreciate any comments and feedback.

References:

Walkthrough: Create a Site Column, Content Type, and List for SharePoint
http://msdn.microsoft.com/en-us/library/ee231593.aspx

SP User Group Jan 2013

SharePoint 2013 Hosted App Development Presentation Deck

I had the pleasure to speak at the January 2013 Toronto SharePoint User Group meeting.

Topic: SharePoint 2013 Hosted App – Deep Dive Implementation

The last few months of I have been working on a photo slider app displaying a photo, title and caption. I wanted to learn the technologies, architecture and tooling. My presentation is to explain my experience and knowledge in making this app. At the same time, I got some great tips and feedback from the group.

Installing SharePoint 2013 Preview “Server Unavailable” Issue

Issue: After Installation of SP 2013 Preview, Central Administration Web Site resulted in error message “Server Unavailable” and also in the Event Logs with “The worker process failed to pre-load .Net Runtime version v4.0.21006.”

Context: I have a VM copy of a bare installation of SP 2010 with Windows Server 2008R2 and SQL Server 2008 R2. My goal is to install and “overwrite” with SP 2013

My steps
1. Install Windows Server 2008 SP1 and SQL Server 2008 SP1 and SP2
2. Install SP 2013 Preview Prequisites
3. Install SP 2013 Preview Setup
4. Configure new server farm installation
5. Configure for Central Administration web site
6. Loading Central Administration web site resulted in issue

I thought that App pool identity credential’s were incorrect but that was not the issue as many would have thought.

Resolution:

1. Go to “C:\Windows\System32\inetsrv\config” and open applicationHost.config
2. Look for <applicationPools>
3. Look for App Pools with name related to SharePoint especially “SharePoint Central Administration”
4. Change managedRuntimeVersion=”v4.0” to “v4.0.30319”
5. Run iisreset

Note: I have tried to change the runtime version through inetmgr UI, but the drop down selection