Friday, September 27, 2013

SharePoint Asynchronous Webpart Load

Issue Description

While working on SharePoint application development, we usually go with the development of custom web parts. These web parts are then placed on the respective pages as per the application requirements.
This is all good and easy from the development side. But looking at this from a performance perspective, the page load time depends on the total load time of the web parts. This load time can be effectively reduced by loading the web parts’ content asynchronously, as below.

Solution

1.       Using XMLHttpRequest in JavaScript

We can use AJAX to load the web part content, a sort of lazy loading, hence making the page load independent of their load times. A combination of client-side and server-side script is used to implement this.
The client-side XMLHttpRequest object is used to exchange data with a server behind the scenes. This means that it is possible to update parts of a web page, without reloading the whole page.
When a request to a server is sent, we want to perform some actions based on the response. The onreadystatechange event is triggered every time the readyState changes. The readyState property holds the status of the XMLHttpRequest, while the status property indicates whether the response was retrieved successfully or not.
In the onreadystatechange event, we specify the processing to be done for the ready server response. When readyState is 4 and status is 200, the response is ready.
Three important properties of the XMLHttpRequest object:
Property
Description
onreadystatechange
Stores a function (or the name of a function) to be called automatically each time the readyState property changes
readyState
Holds the status of the XMLHttpRequest. Changes from 0 to 4:
0: request not initialized
1: server connection established
2: request received
3: processing request
4: request finished and response is ready
Status
200: "OK"
404: Page not found

2.       Using jQuery Ajax

The ajax() method is used to perform an AJAX (asynchronous HTTP) request. The parameters specify one or more name/value pairs for the AJAX request.
Some possible names/values in the table below:
Name
Value/Description
async
A Boolean value indicating whether the request should be handled asynchronous or not. Default is true
beforeSend(xhr)
A function to run before the request is sent
cache
A Boolean value indicating whether the browser should cache the requested pages. Default is true
complete(xhr,status)
A function to run when the request is finished (after success and error functions).
data
Specifies data to be sent to the server
dataType
The data type expected of the server response.
error(xhr,status,error)
A function to run if the request fails.
success(result,status,xhr)
A function to be run when the request succeeds
timeout
The local timeout (in milliseconds) for the request
type
Specifies the type of request. (GET or POST)
url
Specifies the URL to send the request to. Default is the current page
xhr
A function used for creating the XMLHttpRequest object

3.       jQuery $.get() Method

The $.get() method requests data from the server with an HTTP GET request.
This is a shorthand Ajax function, which is equivalent to:
$.ajax({
  url: url,
  data: data,
  success: success,
  dataType: dataType
});
The success callback function is passed the returned data, which will be an XML root element, text string, JavaScript file, or JSON object, depending on the MIME type of the response. It is also passed the text status of the response.

Syntax:
$.get(URL,callback);
The required URL parameter specifies the URL you wish to request. The optional callback parameter is the name of a function to be executed if the request succeeds. The following example uses the $.get() method to retrieve data from a file on the server:

Example for this implementation:
1)       Create a visual webpart NewsFeedWebpart that will display news articles and will also change news dynamically based on the category selected by the user from a drop down menu.
2)       When user selects a category from the dropdown, the getSelectedNewsFeeds() javascript function will be called, specified on client click for dropdown.  This function will create a new XMLHttpRequest object.
3)       Create a request to the AJAXServiceRequest.aspx application page, along with the required query string parameters, as required (action name and category Id, in this case).
4)       Once the response is received, the existing HTML of the news div is replaced with the new HTML fragment.
Add following code in NewsFeedWebpartUserControl.aspx.
a)       Script for XMLHttpRequest in JavaScript

<script language="javascript" type="text/javascript">

    function getSelectedNewsFeeds() {
        var ajaxRequest;
        var dropdown = document.getElementById('<%=ListSelectedNewsCat.ClientID %>');
        var filterVal = dropdown.options[dropdown.selectedIndex].value;

var ajaxDisplay = document.getElementById('<%=divNews.ClientID %>');

//This is to indicate to user about the processing
ajaxDisplay.innerHTML = 'Loading...';

if (window.XMLHttpRequest)
{ // code for IE7+, Firefox, Chrome, Opera, Safari
  ajaxRequest =new XMLHttpRequest();
}
else
{ // code for IE6, IE5
  ajaxRequest = new ActiveXObject("Microsoft.XMLHTTP");
}       

        ajaxRequest.onreadystatechange = function () {
            if (ajaxRequest.readyState == 4 && xmlhttp.status == 200) {
                var response = ajaxRequest.responseText;
                ajaxDisplay.innerHTML = response;
            }
        }

        var queryString = "?queryaction=ProcessForNewsFeed&category=" + filterVal;
        ajaxRequest.open("GET", "/_layouts/AJAXServiceRequest.aspx" + queryString, true);
        ajaxRequest.send(null);
    }
 
</script>

b)       Script for  $.ajax() in jQuery

<script language="javascript" type="text/javascript">

    function getSelectedNewsFeeds() {
     
    var filterVal = $("[id$='ListSelectedNewsCat'] option:selected").val();
    var ajaxDisplay = $("[id$='divNews']");

    //This is to indicate to user about the processing
    ajaxDisplay.html('Loading...');

    var queryString = "?queryaction=ProcessForNewsFeed&category=" + filterVal;
    var requestString = "/_layouts/AJAXServiceRequest.aspx" + queryString;

    $.ajax({
       url: requestString,
       success: function (result) {
           ajaxDisplay.html(result);
       }
    });
    }
 
</script>

c)       Script for $.get() in jQuery

<script language="javascript" type="text/javascript">

   function getSelectedNewsFeeds() {

        var filterVal = $("[id$='ListSelectedNewsCat'] option:selected").val();
        var ajaxDisplay = $("[id$='divNews']");

        //This is to indicate to user about the processing
        ajaxDisplay.html('Loading...');

        var queryString = "?queryaction=ProcessForNewsFeed&category=" + filterVal;
        var requestString = "/_layouts/AJAXServiceRequest.aspx" + queryString;

        $.get(requestString, function (data, status) {
            if(status == "success")
            {
                ajaxDisplay.html(data);
            }
        });
   }
 
</script>


HTML snippet to add dropdown and div on the page

<div>
    <asp:DropDownList ID="ListSelectedNewsCat" runat="server" onclientclick=" getSelectedNewsFeeds();” DataTextField="Title" DataValueField="ID">
    </asp:DropDownList>
</div>
<div class="divNews" runat="server" id="divNews">
</div>


5)       Get query string parameters in the server side script and call appropriate method to generate news feed HTML for selected category.
For this, create an application page AJAXServiceRequest.aspx in layouts folder. This page will be called by all AJAX requests. Add following code in AJAXServiceRequest.aspx.cs to format the HTML and return it as response for the request.
Return final html using AntiXss.GetSafeHtmlFragment.
AntiXss.GetSafeHtmlFragment: Returns well-formed HTML fragments (does not try and add <html> or <body> tags).

protected void Page_Load (object sender, EventArgs e)
{

//// Process For News Feed
if (Request.QueryString[“queryaction”] != null &&   Request.QueryString[“queryaction”].ToString().Equals(“ProcessForNewsFeed”))
     {
                string formattedText = string.Empty;
                string category = Request.QueryString[“category”];
                formattedText = NewsFeedHelper.ProcessMainNews(category);
                Response.Write(formattedText);
                Response.End();
     }
}
public static string ProcessMainNews (string id)
{
//// code to generate news feed html
                return AntiXss.GetSafeHtmlFragment(html);
}