Showing posts with label jQuery. Show all posts
Showing posts with label jQuery. Show all posts

Saturday, June 16, 2012

OData : Cross domain OData request using jQuery, JSONP

I've been doing a POC where I had a situation to make a request to a OData Service (service which exposed the data as OData  - Open Data Protocol) using jQuery. During my initial attempt, I got an error from the web browser which is due to the same origin policy of the web browser.  Usually Client side (request from browser using javascript) cannot be allowed to make a request to a resource that exists in another domain of which HTML <script> element is an exception. In such cases JSONP (JSON with padding) and CORS (Cross Origin Resource Sharing) is made use. Among the two, JSONP requests are commonly used right now and CORS is expected to catch the mass. In this post we'll focus on the JSONP model request for OData Service.

To get a quick idea on the need for JSONP and how that stuff works, please have a quick glimpse here.  The code snippets I represent in this post will use the Nerd Dinner OData Feed for easier understanding.

After deciding to make the JSONP request, I used jQuery.ajax() method with url : http://www.nerddinner.com/Services/OData.svc/Dinners?$format=json & jsonpCallback option which resulted in error. Upon analyzing it, found out the error is "Uncaught SyntaxError: Unexpected token : " and the error is because the returned data is a simple JSON format and not wrapped with the callback method which is expected. Then I searched and found out the right format to make the OData JSONP request. The option is available as a OData query $callback.

Following are the three different alterations we can do to make a cross domain OData request using jQuery and JSONP.

Snippet 1:

$(document).ready(function () {

    $.ajax({
        url: 'http://www.nerddinner.com/Services/OData.svc/Dinners?$format=json&$callback=nerdCallback',
        dataType: "jsonp"
    });
});

function nerdCallback(result) {
    //Manipulate the resultant here....
    alert("Result count : " + $(result["d"]["results"]).length);
}

Snippet 2:

$(document).ready(function () {

    $.ajax({
        url: 'http://www.nerddinner.com/Services/OData.svc/Dinners?$format=json&$callback=?',
        dataType: "jsonp",
        jsonpCallback: "nerdCallback"
    });
});

function nerdCallback(result) {
    //Manipulate the resultant here....
    alert("Result count : " + $(result["d"]["results"]).length);
}

Snippet 3:

$(document).ready(function () {

    $.ajax({
        url: 'http://www.nerddinner.com/Services/OData.svc/Dinners?$format=json&$callback=?',
        dataType: "jsonp",
        success: function (result) {
            //Manipulate the resultant here....
            alert("Result count : " + $(result["d"]["results"]).length);
        }
    });
});

References:
http://en.wikipedia.org/wiki/JSONP
http://msdn.microsoft.com/en-us/library/ee834511.aspx
http://www.odata.org/

Sunday, August 14, 2011

ASP.NET MVC 3 and jQuery templates - Part2

In part 1, we saw the basic implementation of jQuery template with the help of html file. In this part we'll look into a simple implementation using ASP.NET MVC 3. Following few steps will define the steps I've followed to create the sample which helps readers to dive into the code with better understanding.

First created an empty ASP.NET MVC3 web application. Then created a model by adding an Employee class in the Models folder.

Snippet 1(Model - Employee)

public class Employee
{
    public string EmployeeId { get; set; }
    public string EmployeeName { get; set; }
    public int Experience { get; set; }
    public string Designation { get; set; }
}

For the purpose of this example have created a SampleData class in the Models folder.

Snippet 2 (SampleData)

public class SampleData
{
    #region Class Level Variables
    private List<Employee> projectMemberList;
    #endregion

    #region Properties
    public List<Employee> ProjectMemberList
    {
        get
        {
            if (projectMemberList == null)
            {
                LoadProjectMemberList();
            }
            return projectMemberList;
        }
        set { projectMemberList = value; }
    }
    #endregion

    #region Public Methods
    public IEnumerable<Employee> GetProjectMembersWithExpGreaterThan(int experience)
    {
        return ProjectMemberList.Where(emp => emp.Experience > experience);
    }
    #endregion

    #region Private Methods
    private void LoadProjectMemberList()
    {
        projectMemberList = new List<Employee>();

        projectMemberList.Add(new Employee { EmployeeId = "E1001", EmployeeName = "Member1", Experience = 7, Designation = "Project Lead" });
        projectMemberList.Add(new Employee { EmployeeId = "E1002", EmployeeName = "Member2", Experience = 4, Designation = "SSE" });
        projectMemberList.Add(new Employee { EmployeeId = "E1003", EmployeeName = "Member3", Experience = 9, Designation = "Tech Lead" });
        projectMemberList.Add(new Employee { EmployeeId = "E1004", EmployeeName = "Member4", Experience = 3, Designation = "SE" });
        projectMemberList.Add(new Employee { EmployeeId = "E1005", EmployeeName = "Member5", Experience = 5, Designation = "Team Lead" });

    }
    #endregion

}

Next in the Controllers folder have created a ProjectMemberDetailsController.

Snippet 3 (ProjectMemberDetailsController)

public class ProjectMemberDetailsController : Controller
{
    
    public ActionResult ProjectMemberList()
    {
        return View();
    }

    [HttpPost]
    public JsonResult ProjectMemberList(string experience)
    {
        SampleData sampleData = new SampleData();

        if (string.IsNullOrEmpty(experience) || experience.Equals("All"))
        {
            return Json(sampleData.ProjectMemberList, JsonRequestBehavior.AllowGet);
        }
        else
        {
            return Json(sampleData.GetProjectMembersWithExpGreaterThan(int.Parse(experience)), JsonRequestBehavior.AllowGet);
        }

    }
}

Finally added a ProjectMemberList.cshtml file under the Views/ProjectMemberDetails folder.

Snippet 4 (ProjectMemberList)   

@model String
@{
    ViewBag.Title = "ProjectMemberList";
}
<h2>
    ProjectMemberList</h2>
<script src="@Url.Content("~/Scripts/jquery.unobtrusive-ajax.js")" type="text/javascript"></script>
<script src="http://ajax.aspnetcdn.com/ajax/jquery.templates/beta1/jquery.tmpl.js"
    type="text/javascript"></script>

<script id="memberDetailTemplate" type="text/html">
    <tr>
        <td>
            ${EmployeeName}
        </td>
        <td>
            ${Experience}
        </td>
        <td>
        {{if Designation == 'Tech Lead'}}
        <span style="color:Green; font-weight: bold;">${Designation}</span>
        {{else}}
        <span style="color:Blue;">${Designation}</span>
        {{/if}}            
        </td>
    </tr>  
</script>
<script type="text/javascript">
    $(document).ready(function () {
        $.ajax({ type: "POST", url: "ProjectMemberDetails/ProjectMemberList", success: jsonProjectMemberListSuccessCallback });
    });

    function jsonProjectMemberListSuccessCallback(data) {
        var targetElement = $('#memberData');

        targetElement.empty();

        $("#memberDetailTemplate").tmpl(data).appendTo('#memberData');
    }
</script>
<div id="loadingFeedback" style="display: none; color: Red; font-weight: bold;">
    <p>
        Loading details....
    </p>
</div>
@using (Ajax.BeginForm(new AjaxOptions
{
    LoadingElementId = "loadingFeedback",
    OnSuccess = "jsonProjectMemberListSuccessCallback",
    Url = Url.Action("ProjectMemberList")
}))
{
    <table border="0" cellpadding="0" cellspacing="5">
        <thead>
            <tr style="font-weight: bold;">
                <td>
                    Member Name
                </td>
                <td>
                    Experience
                </td>
                <td>
                    Designation
                </td>
            </tr>
        </thead>
        <tbody id="memberData">
        </tbody>
    </table>
    <p>
        @Html.Label("", "Member with experience greater than : ")
        @Html.DropDownList("experience", new SelectList(new[] { "All", "1", "2", "3", "4", "5", "6", "7", "8", "9" }, (Model ?? "All")))
        <input type="submit" name="Submit" value="Submit" />
    </p>   
   
}

The ProjectMemberList view will automatically use the /Views/Shared/_Layout.cshtml as the layout/master page which is specified in the /Views/_ViewStart.cshtml file.

In order to run invoke the specific action by default, have modified the RegisterRoutes method of MvcApplication class in the Global.asax.cs file as follows:

Snippet 5 (Default Route - Global.asax.cs)

public static void RegisterRoutes(RouteCollection routes)
{
    routes.IgnoreRoute("{resource}.axd/{*pathInfo}");

    routes.MapRoute(
        "Default", // Route name
        "{controller}/{action}/{id}", // URL with parameters
        new { controller = "ProjectMemberDetails", action = "ProjectMemberList", id = UrlParameter.Optional } // Parameter defaults
    );

}

After doing the above changes, when the application is run, the URL ProjectMemberDetails/ProjectMemberList is invoked which in turn invokes the ProjectMemberList action in the ProjectMemberDetailsController. The ProjectMemberList() action returns the related view and this execution happens when the page is first rendered. When the page is rendered, the script we specified in the $(document).ready(...); will be executed. Since we've made a jQuery AJAX call over there with type "POST" and URL("ProjectMemberDetails/ProjectMemberList"), it causes an AJAX call over our action ProjectMemberList(string experience) with attribute [HttpPost] which returns the required result in JSON format. The jQuery script, handles the returned result with the success callback method we specified, which renders the data using the jQuery template on the client side.

The main thing need to be noted is the ProjectMemberList(string experience) action in the ProjectMemberDetailsController which returns the result in the JSON format(JsonResult). Also our view is designed in such a way that the specific action is invoked only using the AJAX call.

You might have also observed that, a predefined dropdown list and a submit button are accompanied inside the AJAX Form represented by @using (Ajax.BeginForm(..)){...} with relevant AjaxOptions as parameter. This AJAX Form ensures that every submit made through the Form will be made as AJAX request. Hence if any submit is made through the submit button click, the ProjectMemberList action specified in the AjaxOptions is invoked passing the option selected in the dropdown list. It is the ASP.NET MVC framework that renders the parameter value, since the parameter specified is same as the name of the dropdown list.

Summary
As a general practice, in the above specified scenarios, the first time when the view is rendered (as the AJAX call won't happen), we'll be using the partial view for rendering the templated region and as we used AJAX Forms, further submit will be directed as AJAX calls. But here, I've specified an example of making an AJAX request from jQuery inside the $(document).ready(...);, because several developers having questions about raising the AJAX request during the initial rendering of the view, which is also answered here.

References
http://api.jquery.com/jQuery.ajax/


Update - 05Nov2011 (Representing the implementation with {{each}} template tag and handling date)

Based on the question asked, I'll specify the modification need to be done to the snippets above in order to see the implementation of {{each}} template tag and displaying date values.

Scenarios included : Have added two more columns Technical Expertise and Date of Joining for display and modified the sample data based on that.
In the case of technical expertise, I receive the list of string values and display each item in the list inside the div element.
In the case of joining date, the received date need to be converted to Date() type in javascript and then formatted accordingly. There exists several ways to achieve that. For this sample I'll use the way specified in one of the stackoverflow.com questions. Here are the refered links - http://stackoverflow.com/questions/206384/how-to-format-a-json-date and http://blog.stevenlevithan.com/archives/date-time-format.

Following are the changes need to be done in the above specified snippets to view the updated example.

Changes in Snippet 1(Model - Employee)
Add two more properties.

public class Employee
{
    public string EmployeeId { get; set; }
    public string EmployeeName { get; set; }
    public int Experience { get; set; }
    public string Designation { get; set; }
    public List<string> TechnicalExpertise { get; set; }
    public DateTime JoiningDate { get; set; }
}

Changes in Snippet 2 (SampleData)
Alter the LoadProjectMemberList methods as follows.

private void LoadProjectMemberList()
{
    projectMemberList = new List<Employee>();

    projectMemberList.Add(new Employee { EmployeeId = "E1001", EmployeeName = "Member1", Experience = 7, Designation = "Project Lead", TechnicalExpertise = new List<string> { "ASP.NET", "C#", "Silverlight" }, JoiningDate = DateTime.Now.AddYears(-4) });
    projectMemberList.Add(new Employee { EmployeeId = "E1002", EmployeeName = "Member2", Experience = 4, Designation = "SSE", TechnicalExpertise = new List<string> { "ASP.NET", "C#" }, JoiningDate = DateTime.Now.AddYears(-2) });
    projectMemberList.Add(new Employee { EmployeeId = "E1003", EmployeeName = "Member3", Experience = 9, Designation = "Tech Lead", TechnicalExpertise = new List<string> { "ASP.NET", "C#", "Silverlight" }, JoiningDate = DateTime.Now.AddYears(-6) });
    projectMemberList.Add(new Employee { EmployeeId = "E1004", EmployeeName = "Member4", Experience = 3, Designation = "SE", TechnicalExpertise = new List<string> { "ASP.NET", "VB.NET" }, JoiningDate = DateTime.Now.AddYears(-3) });
    projectMemberList.Add(new Employee { EmployeeId = "E1005", EmployeeName = "Member5", Experience = 5, Designation = "Team Lead", TechnicalExpertise = new List<string> { "ASP.NET", "C#" }, JoiningDate = DateTime.Now.AddYears(-4) });

}

Changes in Snippet 4 (ProjectMemberList)
Download and add the "date.format.js" file from http://blog.stevenlevithan.com/archives/date-time-format. Then add the script reference in the page.
In our case it is


<script type="text/javascript" src="@Url.Content("~/Content/MVC3TestApp2/Scripts/date.format.js")"></script>

Replace the related matching items with the following snippets.
Replacement 1:

<script id="memberDetailTemplate" type="text/html">
    <tr>
        <td>
            ${EmployeeName}
        </td>
        <td>
            ${Experience}
        </td>
        <td>
        {{if Designation == 'Tech Lead'}}
        <span style="color:Green; font-weight: bold;">${Designation}</span>
        {{else}}
        <span style="color:Blue;">${Designation}</span>
        {{/if}}            
        </td>
        <td>
        {{each(i,technology) TechnicalExpertise}}
            <div style="font-style:italic;border-width : 1px; border-style: dotted;">
            ${i + 1}. ${technology}
            </div>
        {{/each}}
        </td>
        <td>
            ${FormattedDate}
        </td>
    </tr>  
</script>

Replacement 2:

function jsonProjectMemberListSuccessCallback(data) {
    var targetElement = $('#memberData');

    targetElement.empty();
    
    for (var i in data) {
        var date = eval(data[i].JoiningDate.replace(/\/Date\((\d+)\)\//gi, "new Date($1)"));
        var formattedDate = date.format("m/dd/yyyy");
        data[i].FormattedDate = formattedDate;
    }

    $("#memberDetailTemplate").tmpl(data).appendTo('#memberData');
}


Replacement 3:

<table border="0" cellpadding="0" cellspacing="5">
    <thead>
        <tr>
            <th>
                Member Name
            </th>
            <th>
                Experience
            </th>
            <th>
                Designation
            </th>
            <th>
                Technical Expertise
            </th>
            <th>
                Date of Joining
            </th>
        </tr>
    </thead>
    <tbody id="memberData">
    </tbody>
</table>


Now in the resultant you can see the respecitve columns related values.

Monday, March 21, 2011

jQuery event subscription and UpdatePanel(AsyncPostBack)

We usually bind the events for controls to be handled with jQuery in the $(document).ready. But when I tried to try the same for the controls inside the UpdatePanel, I caught up with an issue. The event hooking works fine until the AsyncPostBack happens, but not after that. Its clear that the initial subscription is nomore valid, for which I searched for that and found out in the web.

PageRequestManager

The endRequest event of the PageRequestManager will provide the right space to solve the issue. We need to resubscribe to the events again upon AsyncPostBack. As per the msdn documentation, the endRequest event is Raised after an asynchronous postback is finished and control has been returned to the browser.
The following snippet will do this for you.

Snippet 1

$(document).ready(function () {
    initiateBinding();

    Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

});

function EndRequestHandler(sender, args) {
    initiateBinding();
}

function initiateBinding() {
    //Do the subscription here.
}


Have specified an example below which describes the above specified solution.

Snippet 2

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="PageRequestManager.aspx.cs"
    Inherits="PageRequestManager" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
    <script type="text/javascript">

        $(document).ready(function () {
            initiateBinding();

            Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

        });

        function initiateBinding() {
            $("input[id$='ClientClickButton']").click(function (event) {
                event.preventDefault();

                alert("Click event handling from jQuery");
            });
        }
        
        function EndRequestHandler(sender, args) {
            if (args.get_error() == undefined) {
                initiateBinding();
            }
            else {
                var errorMessage = args.get_error().message;
                args.set_errorHandled(true);

                //Do error handling here.
                alert("Error : " + errorMessage);
            }
        }
        
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <asp:ScriptManager ID="ScriptManager1" runat="server">
    </asp:ScriptManager>
    <div>
        <asp:UpdatePanel ID="UpdatePanel1" runat="server">
            <ContentTemplate>
                <asp:Button ID="ClientClickButton" runat="server" Text="Client Click Button" />
                <br />
                <br />
                <asp:Button ID="AsyncPostBackTestButton" runat="server" Text="Async PostBack Test Button" />
            </ContentTemplate>
            <Triggers>
                <asp:AsyncPostBackTrigger ControlID="AsyncPostBackTestButton" EventName="Click" />
            </Triggers>
        </asp:UpdatePanel>
    </div>
    </form>
</body>
</html>


REFERENCE
http://msdn.microsoft.com/en-us/library/bb383810.aspx

Monday, February 28, 2011

Check / Uncheck all check boxes in ASP.Net using jQuery

jQuery make our scripting(javascript) life simple. One of the powerful things with jQuery is its Selectors. In ASP.Net while displaying list of data using GridView, DataList, ListView or Repeater control, one of the frequently expected thing is the check all option which is expected to happen on the client side.

To achieve those two things need to be done.

Case 1 : Upon the click of the header check box (CheckAll), all the row check boxes' status need to be changed based on the header check box. Case 2 : Upon click of the row check boxes, header check box status need to be set as checked when all of the row check boxes are checked sand vice versa. Following are the sample code to achieve that.

Snippet 1

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="CheckAllWithJQuery.aspx.cs" Inherits="CheckAllWithJQuery" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>CheckAll check boxes in DataList</title>
    <script type="text/javascript" src="http://code.jquery.com/jquery-1.5.1.min.js"></script>
    <script type="text/javascript">

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

        function initiateCheckAllBinding() {
            var headerCheckBox = $("input[id$='HeaderCheckBox']");
            var rowCheckBox = $("#StudentTable input[id*='RowItemCheckBox']");

            headerCheckBox.click(function (e) {                
                rowCheckBox.attr('checked', headerCheckBox.is(':checked'));
            });

            // To select CheckAll when all Item CheckBox is selected and 
            // to deselect CheckAll when an Item CheckBox is deselected.     
            rowCheckBox.click(function (e) {
                var rowCheckBoxSelected = $("#StudentTable input[id*='RowItemCheckBox']:checked");

                if (rowCheckBox.length == rowCheckBoxSelected.length) {
                    headerCheckBox.attr("checked", true);
                }
                else {
                    headerCheckBox.attr("checked", false);
                }
            });
            $("table[id$='StudentDataList'] input[id*='RowItemCheckBox']")
        }       
         
    </script>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    <asp:Repeater ID="StudentRepeater" runat="server">
            <HeaderTemplate>
                <table border="1" cellpadding="5" cellspacing="0" id="StudentTable">
                    <tr>
                        <td>
                            <asp:CheckBox ID="HeaderCheckBox" runat="server" />
                        </td>
                        <td>
                            <b>Id</b>
                        </td>
                        <td>
                            <b>Name</b>
                        </td>
                    </tr>
            </HeaderTemplate>
            <ItemTemplate>
                <tr>
                    <td>
                        <asp:CheckBox ID="RowItemCheckBox" runat="server" />
                    </td>
                    <td>
                        <asp:Literal ID="IdLiteral" runat="server" Text='<%# Eval("Id") %>'></asp:Literal>
                    </td>
                    <td>
                        <asp:Literal ID="NameLiteral" runat="server" Text='<%# Eval("Name") %>'></asp:Literal>
                    </td>
                </tr>
            </ItemTemplate>
            <FooterTemplate>
                </table>
            </FooterTemplate>
        </asp:Repeater>
    </div>
    </form>
</body>
</html>

In the above snippet you find that I've used a repeater control which I've specified a template in such a way the final will output will be of a table structure with id "StudentTable". Also I've specified the header check box with id "HeaderCheckBox" and row check box with id "RowItemCheckBox".

In jQuery, in order to get the instance of the header check, I've used "Attribute Ends With Selector".
$("input[id$='HeaderCheckBox']")
Similarly for getting the instances of row check boxes, I've used "Descendant Selector", that is row item check box selector that is descendant of element with id as "StudentTable".
$("#StudentTable input[id*='RowItemCheckBox']")
This works fine for the above provided template. What need to be done if we have a GridView or DataList? Just we need to identify the html rendering of that control and tweek the selector matching that. To be more clear, in general, GridView and DataList will be rendered as html table by default. Think of a case that you have a DataList with id "StudentDataList", then you can modify the selector as
$("table[id$='StudentDataList'] input[id*='RowItemCheckBox']")
SUMMARY
To achieve Case 2, simply I'm evaluating based on the count of number of row check boxes with that of the number selected row check boxes.

REFERENCE
http://api.jquery.com/category/selectors/
http://www.devcurry.com/2009/08/check-uncheck-all-checkboxes-in-aspnet.html
 
Creative Commons License
This work by Tito is licensed under a Creative Commons Attribution 3.0 Unported License.