Jquery Datatable: Implementing Load on demand Server side with PageMethod/WebMethod

Jquery Datatable is one of the best plugin which we can use in alternate of GridView.

Though there are so many examples available at https://www.datatables.net/ for sever side processing with .txt or .php as AjaxSource. But there isn’t full example on achieving Load on demand with server side processing in asp.net by using PageMethod/WebMethod.

So, if you are looking for similar solution, please walk through below sample code.

Important: Before jumping into the code, it can be beneficial if you have some knowledge of datatable terminology and parameters we send to PageMethod/WebMethod or Parameters we return from PageMethod/WebMethod.

Click here for more information on Parameters sent to the server and returned from server.

Click here for more information on Datatable Methods.

CSS & JS

<link href="//cdn.datatables.net/1.10.7/css/jquery.dataTables.min.css" rel="stylesheet" type="text/css" />
<script src="//cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js" type="text/javascript"></script>

Default.aspx

<form id="form1" runat="server">
        <div id="container">
            <table id="JQtblUsers" class="display">
                <thead>
                    <tr>
                        <th>Email</th>
                        <th>First Name</th>
                        <th>Last Name</th>
                        <th>Company</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </div>
    </form>
<script type="text/javascript">
        $(document).ready(function () {
            var oTable = $('#JQtblUsers').dataTable({
                "paging": true,
                "sPaginationType": "full_numbers",
                "bServerSide": true,
                "sAjaxSource": "Default3.aspx/GetUsers",
                "fnServerData": function (sSource, aoData, fnCallback) {
                    logsRequest = $.ajax({
                        type: "POST",
                        url: sSource,
                        data: "{'sEcho': '" + getValueFromArray(aoData, "sEcho") + "', 'sSearch': '" + getValueFromArray(aoData, "sSearch")
                            + "', 'iDisplayLength': '" + getValueFromArray(aoData, "iDisplayLength") + "', 'iDisplayStart': '" + getValueFromArray(aoData, "iDisplayStart")
                            + "', 'iColumns': '" + getValueFromArray(aoData, "iColumns") + "', 'iSortingCols': '" + getValueFromArray(aoData, "iSortingCols")
                            + "', 'sColumns': '" + getValueFromArray(aoData, "sColumns") + "'}",
                        contentType: "application/json; charset=utf-8",
                        dataType: "json",
                        success: function (data) {
                            var json = jQuery.parseJSON(data.d);
                            fnCallback(json);
                        }
                    });
                },
                "aoColumns": [
                                {
                                    "sName": "Email",
                                    "bSearchable": true,
                                    "bSortable": true,
                                    "fnRender": function (oObj) {
                                        return '<a href=\"mailto:' + oObj.aData[0] + '\">' + oObj.aData[0] + '</a>';
                                    }
                                },
                                {
                                    "sName": "FirstName",
                                    "bSearchable": true,
                                    "bSortable": true,
                                    "fnRender": function (oObj) {
                                        return oObj.aData[1];
                                    }
                                },
                                {
                                    "sName": "LastName",
                                    "bSearchable": true,
                                    "bSortable": true,
                                    "fnRender": function (oObj) {
                                        return oObj.aData[2];
                                    }
                                },
                                {
                                    "sName": "Company",
                                    "bSearchable": true,
                                    "bSortable": true,
                                    "fnRender": function (oObj) {
                                        return oObj.aData[3];
                                    }
                                }
                ],
            });
        });

        function getValueFromArray(aoData, Key) {
            for (i = 0; i < aoData.length; i++) {
                if (aoData[i].name == Key) {
                    return aoData[i].value;
                }
            }
        }
    </script>

Default.aspx.cs

#region Class

public class Employee
{
    public string Email { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public string Company { get; set; }
}

#endregion Class
#region Methods

    [WebMethod]
    public static string GetUsers(string sEcho, string sSearch, int iDisplayLength, int iDisplayStart, int iColumns, int iSortingCols, string sColumns)
    {
        var echo = int.Parse(sEcho);
        var displayLength = iDisplayLength;
        var displayStart = iDisplayStart;

        var records = GetRecordsFromDatabaseWithFilter(sSearch);
        if (records == null)
            return string.Empty;

        var itemsToSkip = displayStart == 0 ? 0 : displayStart + 1;
        var pagedResults = records.Skip(itemsToSkip).Take(displayLength).ToList();
        var hasMoreRecords = false;

        var sb = new StringBuilder();
        sb.Append(@"{" + "\"sEcho\": " + echo + ",");
        sb.Append("\"recordsTotal\": " + records.Count + ",");
        sb.Append("\"recordsFiltered\": " + pagedResults.Count + ",");
        sb.Append("\"iTotalRecords\": " + records.Count + ",");
        sb.Append("\"iTotalDisplayRecords\": " + records.Count + ",");
        sb.Append("\"aaData\": [");
        foreach (var result in pagedResults)
        {
            if (hasMoreRecords)
                sb.Append(",");

            sb.Append("[");
            sb.Append("\"" + result.Email + "\",");
            sb.Append("\"" + result.FirstName + "\",");
            sb.Append("\"" + result.LastName + "\",");
            sb.Append("\"" + result.Company + "\"");
            sb.Append("]");
            hasMoreRecords = true;
        }
        sb.Append("]}");
        return sb.ToString();
    }

    public static List<Employee> GetRecordsFromDatabaseWithFilter(string search)
    {
        List<Employee> Employees = new List<Employee>();
        for (int i = 0; i <= 5; i++)
        {
            Employees.Add(new Employee { Email = "psatikunvar7@gmail.com", FirstName = "Pratik", LastName = "Satikunvar", Company = "Apple" + i });
            Employees.Add(new Employee { Email = "psatikunvar7@gmail.com", FirstName = "Pratik", LastName = "Satikunvar", Company = "Microsoft" + i });
            Employees.Add(new Employee { Email = "psatikunvar7@gmail.com", FirstName = "Pratik", LastName = "Satikunvar", Company = "Google" + i });
            Employees.Add(new Employee { Email = "psatikunvar7@gmail.com", FirstName = "Pratik", LastName = "Satikunvar", Company = "Amazon" + i });
        }
        return Employees.Where(a => a.Company.ToLower().Contains(search.ToLower())).ToList();
    }

    #endregion Methods

I hope it was interesting and useful.

Please share any real time issue or feature you have faced or implemented.

Thank You 🙂

Custom Validator: validation with ajax call

Hello/Namaste all,

Nowadays we do not like to work with postbacks and update panel, this is the time of Jquery/Javascript and Ajax.

So, sometimes we wanted to validate a form from javascript itself without doing postback by calling some method in code behind. So, today we will see how we can validate a form from javascript by doing ajax call to code behind. And what kind of difficulties we will face.

Starting with, we can use custom validator’s client side javascript function, Ajax call ($ajax() function), PageMethod/WebMethod to validate from javascript having actual logic in code behind PageMethod/WebMethod.

Please see below:

  Default.aspx

<form id="form1" runat="server">
        <div>
            <asp:TextBox ID="txtEmail" runat="server"></asp:TextBox>
            <asp:CustomValidator ID="cmtxtForm" runat="server" ClientValidationFunction="OnClientValidate" SetFocusOnError="true"
                ErrorMessage="Email address already exist" ValidateEmptyText="false" ValidationGroup="FormSubmit">
            </asp:CustomValidator>
            <asp:Button ID="btnSubmit" runat="server"  OnClick="btnSubmit_Click" Text="Submit" ValidationGroup="FormSubmit" />
        </div>

        <script type="text/javascript">
            function OnClientValidate(sender, args) {
                var Email = document.getElementById("txtEmail").value;
                if (Email != null && Email != "") {
                    alert(Email);
                    $.ajax({
                        type: "POST",
                        contentType: "application/json; charset=utf-8",
                        url: "Default.aspx/CheckIfEmailExist",
                        data: "{'Email': '" + Email + "'}",
                        dataType: "json",
                        success: function (result) {
                            args.IsValid = result.d;
                            alert(args.IsValid);
                        }
                    });
                }
                else {
                    args.IsValid = false;
                }
                alert(args.IsValid);
            }
        </script>

Default.aspx.cs

    #region PageMethods

    [WebMethod]
    public static bool CheckIfEmailExist(string Email)
    {
        if (Email.ToLower() == "psatikunvar7@gmail.com")
            return false;
        else
            return true;
    }

    #endregion PageMethods

Note: Here, it will call web method and return the result, but if you will check by implementing above code, what you will find is inaccurate args.IsValid in both alert function. No matter what code behind function returns, IsValid will always be true.

This is because, $.ajax() method is by default async method. so, to get the actual response from the PageMethod/WebMethod in ajax function inside client side validation function for custom validator, we need to set option async to false in ajax function as shown below:

 $.ajax({
        type: "POST",
        contentType: "application/json; charset=utf-8",
        url: "Default.aspx/CheckIfEmailExist",
        data: "{'Email': '" + Email + "'}",
        dataType: "json",
        async: false,
        success: function (result) {
            args.IsValid = result.d;
            alert(args.IsValid);
        }
 });

That’s it. Now it should work fine as per value PageMethod/WebMethod is returning.

If you enjoy working with Jquery and Ajax please drop anything you want to share about it in comment. i would like to learn and discuss.

Thank You. Stay Hungry 🙂

Custom Validator: Pass custom parameter to clientside validation function from code behind

Hello readers,

It is common we uses all kind of validators in our daily programming. Custom Validator is one of them and may be very interesting too.

Recently i was needed to add one Custom Validator to the page and i was needed to validate a control dynamically based on condition. so that i was needed to pass some custom parameter from code behind to clientside validation function based on which i will determine which control to validate.

Please refer below code to achieve this:

Default.aspx

 <form id="form1" runat="server">
        <div>
            <asp:TextBox ID="txtForm" runat="server"></asp:TextBox>
            <asp:CustomValidator ID="cmtxtForm" runat="server" ClientValidationFunction="OnClientValidate" SetFocusOnError="true"
                ErrorMessage="Please provide valid value" ValidateEmptyText="false" ValidationGroup="FormSubmit">
            </asp:CustomValidator>
            <asp:Button ID="btnSubmit" runat="server" OnClick="btnSubmit_Click" Text="Submit" ValidationGroup="FormSubmit" />
        </div>
        <script type="text/javascript">
            function OnClientValidate(sender, args) {
                alert(sender.CustomParameter);
                args.IsValid = sender.CustomParameter;
            }
        </script>
    </form>

Default.aspx.cs

 protected void Page_Load(object sender, EventArgs e)
    {
        bool HasQuerystring = false;
        if (!string.IsNullOrEmpty(Request.QueryString["q"]))
            HasQuerystring = true;
        Page.ClientScript.RegisterExpandoAttribute(cmtxtForm.ClientID, "CustomParameter", HasQuerystring.ToString(), false);
    }

It was so easy right? Thank you 🙂

Sitecore 8: Versioned Layouts

Hello Everyone,

When i was exploring sitecore 8 version i found a very much helpful feature that sitecore team introduced is Versioned Layout.

Versioned Layout means you can have language specific layout. which you can specify for different language version of an item. For ex: English version of an item can have English version Layout, German version of an item can have German version Layout.

Why We required it?

As sitecore already provides language version for an item, so we can easily achieve multilingual site in sitecore. so, question comes in mind when we can use versioned layout?

When there is multilingual site with same layout structure we do not required versioned layout. but if specific to some language we want to change the whole UI of the site than we can now achieve it with the versioned layout.

As mentioned above, Language Version Layouts gives you the ability to create different layout definitions for each language version for an item. In Sitecore 8 there is now a Shared Layout tab and a Final Layout tab in the presentation details dialogue of an item as shown in below:

PresentationDetails

The Shared Layout section stores the renderings that are shared across all language versions of an item as it is currently running before sitecore 8. but if you have specified different renderings for specific language than you can found it at Final Layout tab. Thus the Final Layout details pane displays the final combination of presentation details, and determines what is rendered when viewing the current version of the item.

So, Versioned layouts make it possible to specify different layouts for different versions and languages of the same item.

as per discussion above, we can use versioned layouts when we need to have:

  • Have different layouts for different languages.
  • Publish a specific version with its own layout for a specific period.

Sitecore uses versioned layouts internally for various content-testing features, for example, to provide cross-language testing.

In the Standard fields of a versioned item, in the Layout section, two fields are for layouts:

  • __Renderings – a shared field where you specify the common layout for all languages and versions of the item.
  • __Final Renderings – a versioned field where you specify individual layouts for languages and versions of the item.

If you are interested, the following flow diagram shows in detail how Sitecore resolves which final layout to use for an item:

Versioned Layouts-Picture 1-rId9-1950865309

Click here to see the changes in sitecore api due to versioned layouts.

I will share a practical example on achiving different UI for different language version soon, till that you can share your knowledge on versioned layouts.

Do enjoy sitecore 🙂