Customizing Asp.net menu control to use jQuery,Superfish and CSS Friendly Control Adapters

I have used Asp.net menu controls in a lot of applications earlier but now a days with so many new things and controls asp.net menu control seems to lag in many basic functionalities, there are various other options available to be used as a replacement of the asp.net menu control. But what i loved with the asp.net menu control was its seamless integration with the sitemap datasource and with no hassle i could display whole of my site’s navigation in the menu control by just configuring them in the sitemap file. What i disliked most was that the menu control by default is rendered as a table structure and not as div structure. So to fight this abnormality Microsoft released CSS Friendly Control Adapters so that most of the controls which are rendered as tables can be rendered as a simple and clean div structure. But today i was experimenting with other available options and i found a really cool Superfish jQuery plug-in which we can attach with the asp.net menu control to achieve very cool affects. In this article i will share the steps on how to hook up the Superfish jQuery Menu plug-in into your asp.net website to beautify your asp.net menu control. Also please check various option available with the Superfish plug-in on how it can be customized from the Superfish plug-in website. http://users.tpg.com.au/j_birch/plugins/superfish/#examples Now let’s look at the steps to integrate superfish menu with asp.net menu

Main JavaScript file which is required is superfish.js along with jQuery any version.js rest files are optional and are used if we use some advanced features and customizations with the superfish menu. Main CSS file is superfish.css and for this demo we will modify this file only.

  • After downloading the superfish menu files now we should download the CSS Friendly Adapter from http://cssfriendly.codeplex.com/
  • Place the CSSFriendly.dll into your website bin folder and the CSSFriendlyAdapters.browser in your App_Browser folder.Also remember to comment out the unwanted code from the CSSFriendlyAdapters.browser file as we are dealing only with the menu.
 <controlAdapters>
      <adapter controlType="System.Web.UI.WebControls.Menu"
               adapterType="CSSFriendly.MenuAdapter" />
      <!--adapter controlType="System.Web.UI.WebControls.TreeView"
               adapterType="CSSFriendly.TreeViewAdapter" />
      <adapter controlType="System.Web.UI.WebControls.DetailsView"
               adapterType="CSSFriendly.DetailsViewAdapter" />
      <adapter controlType="System.Web.UI.WebControls.FormView"
               adapterType="CSSFriendly.FormViewAdapter" />
      <adapter controlType="System.Web.UI.WebControls.DataList"
               adapterType="CSSFriendly.DataListAdapter" />
      <adapter controlType="System.Web.UI.WebControls.GridView"
               adapterType="CSSFriendly.GridViewAdapter" />
      <adapter controlType="System.Web.UI.WebControls.ChangePassword"
               adapterType="CSSFriendly.ChangePasswordAdapter" />
      <adapter controlType="System.Web.UI.WebControls.Login"
               adapterType="CSSFriendly.LoginAdapter" />
      <adapter controlType="System.Web.UI.WebControls.LoginStatus"
               adapterType="CSSFriendly.LoginStatusAdapter" />
      <adapter controlType="System.Web.UI.WebControls.CreateUserWizard"
               adapterType="CSSFriendly.CreateUserWizardAdapter" />
      <adapter controlType="System.Web.UI.WebControls.PasswordRecovery"
               adapterType="CSSFriendly.PasswordRecoveryAdapter" /-->
    </controlAdapters>

 

  • Also download the latest version of jQuery, otherwise there is already a version of jQuery in the Superfish package.
  • Now start creating your pages and once you are done , just create a sitemap file to list your whole website.
<?xml version="1.0" encoding="utf-8" ?>
<siteMap xmlns="http://schemas.microsoft.com/AspNet/SiteMap-File-1.0" >
    <siteMapNode url="" title=""  description="">
        <siteMapNode url="Default.aspx" title="Home" description=""/>
        <siteMapNode url="AboutUs.aspx" title="About Us"  description="" />
        <siteMapNode url="Services.aspx" title="Services"  description="" >
            <siteMapNode url="Services/Service1.aspx" title="Service1" description="">
                <siteMapNode url="Services/SubService1.aspx" title="SubService1" description="" />
                <siteMapNode url="Services/SubService2.aspx" title="SubService2" description="" />
                <siteMapNode url="Services/SubService3.aspx" title="SubService3" description="" />
                <siteMapNode url="Services/SubService4.aspx" title="SubService4" description="" />
            </siteMapNode>
            <siteMapNode url="Services/Service2.aspx" title="Service2" description=""/>
            <siteMapNode url="Services/Service3.aspx" title="Service3" description=""/>
            <siteMapNode url="Services/Service4.aspx" title="Service4" description=""/>
        </siteMapNode>
        <siteMapNode url="ContactUs.aspx" title="Contact Us" description=""/>
    </siteMapNode>
</siteMap>
  • After building all this your solution architecture should be like this.

  • Now just the master page configuration is left and you are ready to go then
<%@ Master Language="C#" AutoEventWireup="true" CodeFile="Default.master.cs" Inherits="_Default" %>

<!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 src="Scripts/jquery-1.3.1.js" type="text/javascript"></script>

    <script type="text/javascript" src="Scripts/superfish.js"></script>

    <link type="text/css" href="~/Styles/superfish.css" rel="stylesheet" media="screen"
        runat="server" />
    <link href="Styles/stylemain.css" rel="stylesheet" type="text/css" />

    <script type="text/javascript">
        $(document).ready(function() {
            $('ul.AspNet-Menu').superfish();
        }); 
    </script>

    <asp:ContentPlaceHolder ID="head" runat="server">
    </asp:ContentPlaceHolder>
</head>
<body>
    <form id="form1" runat="server">
    <div id="outerWrapper">
        <div id="header">
            <div id="logo">
                <asp:HyperLink runat="server" NavigateUrl="~/Default.aspx" ID="lnkLogo">    
                </asp:HyperLink>
            </div>
        </div>
        <div id="menu">
            <asp:Menu ID="menuMain" runat="server" DataSourceID="stmpDataSource" Orientation="Horizontal">
            </asp:Menu>
            <asp:SiteMapDataSource ID="stmpDataSource" runat="server" ShowStartingNode="false" />
        </div>
        <div id="mainContent">
            <asp:ContentPlaceHolder ID="ContentPlaceHolder1" runat="server">
            </asp:ContentPlaceHolder>
        </div>
        <div id="footer">
        </div>
    </div>
    </form>
</body>
</html>
  • Important :- By default the Superfish menu css file contains the settings aligned with the ul name sf-menu but the asp.net menu control renders the ul element as AspNet-menu so the important step is to replace all the occurrences of sf-menu in the superfish.css with AspNet-menu to make this menu work.

You can download the sample project and plug and play with the CSS file customized for the asp.net menu if you find difficulties in customizing by your self.

After embedding this in your master page you are done with the settings and customization and you are ready to go, i got this menu in just 15 minutes so simple and so easy.

Working Demo can be seen here http://www.smallworkarounds.net/demos/superfish.demo/

Download the Demo from http://www.smallworkarounds.net/blog/democode/Superfish/Superfish.Demo.zip

Happy Programming!!!!!!!!!!!!!!!!!!!!


Best and simple approach to deal with JSON Dates problem

It might be very frustrating for the guys who decided to try for the so called great revolution using jQuery and asp.net,WCF and Linq in combination. Their adventure with the code might come to an end first on the WCF configuration step or secondly handling the JSON serialization over the wire. Although its really simple to configure the WCF service for your JSON wire transfer requirement but its certainly a little confusing. Another problem which comes after successful configuration of the WCF services with your application is dealing with “Dates”. This problem arises due to the fact that there no javascript literal called Date.Everytime we need a date we create a new object of the date such as:-

Date myDate = new Date(“28/04/2008”);

 

If you write the date like the below given syntax then its not the actual date but its only ordinary string which is of no use as such and does not solve our purpose.

var  myDate = “28/04/2008”;

So the response which you will receive from the server in the JSON format will contain all the dates in a surprising format which can be easily parsed if you are using Micorosft Ajax. But if you are on your own using direct jQuery $ajax() then your life is in trouble.

To workaround this strange Date problem there are few possible ways out of which :-

  1. Parse the response from the server on your own and just use the regex to remove / symbol and then evaluating the date string and converting it to Date($1) and returning this date.Although this method is the so called correct one but still its little too complicated and it seems to be the only standard workaround for solving this problem.Given below is some custom implementation for showing how you can parse and evaluate the response received from the server.Always keep in mind that this code would need some change if you are using some other type of replacer symbols in the JSON,this is just a reference example how to parse the response and then evaluating it to get the correct dates.
  $.ajax({
        type: "POST",
        url: pagePath + "/" + methodName,
        contentType: "application/json; charset=utf-8",
        data: paramList,
        dataType: "text",
        processData: false,
        success: function(msgg) {
            var msg = JSON.parse(msgg, function(key, value) {
                var a;
                if (value != null) {
                    if (value.toString().indexOf('Date') >= 0) {
                        a = /^/Date((-?[0-9]+))/$/.exec(value);
                        if (a) {
                            var dt = new Date(parseInt(a[1], 10));
                            return (dt.getMonth() + 1) + "/" + dt.getDate() + "/" + dt.getFullYear();
                        }
                    }
                    return value;
                }
            });
            successFunction(msg);
        },
        error: function(xhr, status, error) {
            errorFunction(xhr, status, error);
        }
    });

Here in this example we are getting the actual response in the variable msgg.After invoking the success we are parsing the response and then we are searching for the Date values in the string where ever found we are removing the unnecessary symbols / and then parsing the 10 digit date and after this step converting it to actual date and finally returning the value.

Although its a little tricky approach but it works fine,you might have to change the parsing method a little bit as according to your needs to make it work for you.

2.Another simple solution is forget about the Date thing on the client side consider every thing as string on the client and also in your business logic and application layer.Although it doesn’t sounds good and many of us don’t agree on this but its the simplest possible workaround to make things work quickly.

So for achieving this no need to make any changes in your database schema just let the dates be dates only in your schema.

What we will do is we will pass date as string from the UI using BusinessObjects to the Service Layer and DataAccessLayer and after reaching the database we will convert these values back to string and while retrieving those dates from the database they should be converted again back to string so that we are not again running into the same old problem.

Converting date to string while retrieving back the date to be displayed somewhere on the page

CONVERT(CHAR(11),datemodified ,106) AS datemodified,
		CONVERT(CHAR(11),datecreated,106) AS datecreated

There are various other choices available with changing the last code part to receive date in various other formats.

By using this mechanism you will never run into this date trouble again


Error during serialization or deserialization using the JSON JavaScriptSerializer. The length of the string exceeds the value set on the maxJsonLength property.

If you are getting this error, this means you are conceptually doing wrong somewhere.Ideally ajax scenarios are meant only for small to medium server side calls not for getting tons of data, and if you are getting a lot of data using the asynchronous request then you are asking for trouble.So in such scenarios it is best to go and implement server side logic.But what if you are at a stage where you can't change your application logic now.

1.So for such a situation there are two ways either implement paging in getting your data which is very simple if you are using jQuery with any other server side technology. Here is a quick link which tells you how to enable paging and many other things in your custom code. http://localhost/smallworkarounds/index.php/2009/02/28/jquery-aspnet-how-to-implemen/ 2.If you can't enable paging also then you are left with only one other solution which is not so good but if your service call is failing then it will prevent the service call from failing, but if you are loading large amount of data again if you prevent your service call from failing still your browser will go abrupt and seems like it has hanged, but if you still want to try then just make this entry in your web.config file to increase the max-request length of the json-request

<system.web.extensions>
<scripting>
<webServices>
<jsonSerialization maxJsonLength="2147483644"></jsonSerialization>
</webServices>
</scripting>
</system.web.extensions>

Important thing to keep in mind is that the maximum value for the integer field is 2147483647 and this limit has to remain inside the bounds of this value.

Happy Programming!!!


jQuery Leaking Memory:-Be careful while using in big applications

jQuery is very small and useful javascript library which is a must for every developer related to web. I also have used jQuery in my applications a lot, but recently i found that jQuery is leaking memory which is causing trouble to my applications,so be careful while using your selectors and creating dynamic DOM elements by using jQuery.In this article i will discuss the simple ways to track the memory leak in your application and comments related to optimizing and reducing the memory leaks using jQuery are always welcome.

How to detect the memory leak in your web application There is a very simple tool available on web known as "Drip" which can help you to test the memory leaks in your web applications You can download drip from http://www.outofhanwell.com/ieleak/Drip-0.5.exe Documentation of drip is available at https://ieleak.svn.sourceforge.net/svnroot/ieleak/trunk/drip/docs/index.html Drip can catch the memory leaks inside the boundary of Internet Explorer but not outside its boundary. A typical run on one of my applications which is fully jQuery dependent gives me these results. What Drip does is that it unloads the page and checks for the memory leaks which remained even after the page was unloaded. Given below is the drip test on jQuery.com As you can see that jQuery.com also have some memory leak which seems to be a bug to me in jQuery code itself. On some research on net i found that commenting out few lines of code in the jQuery itself can reduce some of the memory leaks in your application but still the memory leak is there and doesn't leave you, might be in future we get some bug fix on this memory leak thing from the jQuery team. The lines of code commenting which can do little magic is found inside the jQuery library

if ( div.attachEvent && div.fireEvent ) {
		div.attachEvent("onclick", function(){
			// Cloning a node shouldn't copy over any
			// bound event handlers (IE does this)
			jQuery.support.noCloneEvent = false;
			div.detachEvent("onclick", arguments.callee);
		});
		div.cloneNode(true).fireEvent("onclick");
	}

If any one of you have some other tips and tricks to reduce this memory leaking and any other way to prevent it.Do post comments to help the community and also help a lot of jQuery developers and jQuery itself to be a bug free library.

Happy Programming!!!


jQuery & asp.net :- How to implement paging,sorting and filtering on a client side grid using asp.net,jTemplates and JSON

This article will concentrate only on high performance grid implementation using jQuery,JSON, .net,Linq.Most of us in our career face a situation when we want performance intensive i.e solutions where high performance is the topmost priority.

In such scenarios Gridview,Repeater and all other server side controls are far too much of a thing and above all if you want paging,sorting and filtering functionality also in them, then instead of becoming high performance it just becomes a creepy solution which we want to say as high performing because we couldn't find out any other solution in the give amount of time.

In this article i will cover almost every aspect which is important i.e "Paging" , "Sorting" , "Filtering" data on basis of columns.Although many of us are using already available handy solutions whose family include Telerik Rad Controls,Infragistics Controls or DevExpress on the server side and other solutions such as Ingrid and FlexiGrid  which are very popular as client side data grids.So the goal of this article is to achieve the same functionalities  which other client side grids achieve. Having your own code with all the scripts written by you and every stored proc which you can understand will give you a lot more confidence in achieving what you want.This little effort will prove far more better then the tedious task in what others have implemented.Here i would like to be as simple as i can although some stored procs might seem a little complex but mind you if you consider yourself a so-so kind of a guy for SQL and stored procs then they are just a piece of cake.

Disclaimer

This implementation is very simple and just act as a beginning to others who want to implement their own logic.First step is always the hard step, so i m trying to provide that first step,although very basic but this article will solve really complex problems.Also this article is not  intended to compete or compare with any of the above mentioned controls, it just speaks of how simple things can be achieved in simple ways.

Content

  1. Creating a stored proc which will support paging.
  2. Creating a stored proc which will support sorting(Here it is implemented with limited functionality,you can extend and make it generic)
  3. Creating a stored proc which supports filtering (Here it is implemented with limited functionality,you can extend and make it generic)
  4. Creating the business objects to retrieve the paged results and also total records this will be a little complex object for JSON to understand but there are very easy ways which we will discuss to achieve this.
  5. Creating a DAO or Data Access Layer which simply consists of a DataContext based call to this stored proc.
  6. Creating a Service Layer function to call this DAO implementation of the Stored Proc.
  7. Calling this Service Layer Function from our Page.Here we are using WebMethods to demonstrate this example, i would prefer you to use WCF Restful services but here i m using this just because there are very few examples using WebMethods and several examples using Web Services,although WebService way is far more better but if you want things to be simple and not want the hassle of extra security for the WebService layer , or your client does not want a web service model then WebMethods approach would be the only other good alternative.
  8. Calling this WebMethod from built in jQuery ajax functions to get a JSON response from the server.
  9. This step will include plugging in your jTemplate or any other templating engine which suits your need and rendering the data with some tweaks using jQuery
  10. That's all what you need to do while implementing your own grid.

1.Creating a stored proc which will support paging,sorting and filtering

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
-- =============================================
-- Author:		Aashish Gupta
-- Create date: Feb 14
-- Description:	Retrieves campaigns for advertiser having paging,sorting and filtering facility
-- =============================================
ALTER PROCEDURE [dbo].[GetCampaignByAdvId]
@pageNum INT ,
@pageSize INT,
@sortColumnName VARCHAR(200),
@sortDirection INT	,
@filterStatusId INT,
@advertiserAccId INT,
@totalRecords INT out	
AS
BEGIN
DECLARE @campaigns TABLE
(CampaignID int,row_num int);
	
	SET NOCOUNT ON;
IF @filterStatusId = 0
BEGIN
	INSERT INTO @campaigns
	SELECT CampaignID,ROW_NUMBER() OVER(
	ORDER BY
	CASE WHEN @sortDirection = 1 THEN NULL			
			WHEN @sortColumnName = 'CampaignName' THEN CONVERT(VARCHAR(200),CampaignName)
			WHEN @sortColumnName = 'CampaignStatusName' THEN CONVERT(VARCHAR(100),CampaignStatusName)
			WHEN @sortColumnName = 'UtcStartDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			WHEN @sortColumnName = 'UtcEndDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			
			ELSE NULL END DESC,
			CASE WHEN @sortDirection <> 1 THEN NULL
			WHEN @sortColumnName = 'CampaignName' THEN CONVERT(VARCHAR(200),CampaignName)
			WHEN @sortColumnName = 'CampaignStatusName' THEN CONVERT(VARCHAR(100),CampaignStatusName)
			WHEN @sortColumnName = 'UtcStartDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			WHEN @sortColumnName = 'UtcEndDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			ELSE NULL END ASC			
	) AS [row_num] FROM dbo.CampaignDetailsView c WHERE c.AdvertiserAccountID = @advertiserAccId
	SET @totalRecords = @@ROWCOUNT
	SELECT c.* FROM dbo.CampaignDetailsView AS c JOIN @campaigns AS tc ON c.CampaignID = tc.CampaignID
	WHERE tc.row_num BETWEEN (((@PageNum - 1) * @PageSize) + 1) 
AND (@PageNum * @PageSize)
ORDER BY tc.row_num

Return @totalRecords
END
ELSE
BEGIN
	INSERT INTO @campaigns
	SELECT CampaignID,ROW_NUMBER() OVER(
	ORDER BY
	CASE WHEN @sortDirection = 1 THEN NULL			
			WHEN @sortColumnName = 'CampaignName' THEN CONVERT(VARCHAR(200),CampaignName)
			WHEN @sortColumnName = 'CampaignStatusName' THEN CONVERT(VARCHAR(100),CampaignStatusName)
			WHEN @sortColumnName = 'UtcStartDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			WHEN @sortColumnName = 'UtcEndDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			
			ELSE NULL END DESC,
			CASE WHEN @sortDirection <> 1 THEN NULL
			WHEN @sortColumnName = 'CampaignName' THEN CONVERT(VARCHAR(200),CampaignName)
			WHEN @sortColumnName = 'CampaignStatusName' THEN CONVERT(VARCHAR(100),CampaignStatusName)
			WHEN @sortColumnName = 'UtcStartDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			WHEN @sortColumnName = 'UtcEndDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			ELSE NULL END ASC			
			) AS [row_num] FROM dbo.CampaignDetailsView c WHERE c.AdvertiserAccountID = @advertiserAccId AND c.CampaignStatusID = @filterStatusId
	SET @totalRecords = @@ROWCOUNT
	SELECT c.* FROM dbo.CampaignDetailsView AS c JOIN @campaigns AS tc ON c.CampaignID = tc.CampaignID
	WHERE tc.row_num BETWEEN (((@PageNum - 1) * @PageSize) + 1) 
AND (@PageNum * @PageSize)
ORDER BY tc.row_num

Return @totalRecords
END
END

This is the whole stored proc actually what you need for implementing paging,sorting and filtering.

Let me go deep and explain what it does.It takes 6 input parameters and one output parameters.

 

@pageNum INT ,
@pageSize INT,
@sortColumnName VARCHAR(200),
@sortDirection INT	,
@filterStatusId INT,
@advertiserAccId INT,
@totalRecords INT out
  • @pageNum refers to the current page no which we will pass every time from the UI.If our current pageSize is 10 on every call we will fetch only 10 records which is very good as far as performance is considered.Another approach here could be to fetch every record from the Database and then use Linq in your application level to fetch 10 records only at your client end.This second approach can be very handy if you are trying to make a generic client side control with this implementation,but if you have full control over your Database and stored procs i.e if you are developing whole application for your client from UI to Database then the approach i have discussed would be the best but if you dont have access to modifying and creating the stored procs then you can use direct Linq to get the limited amount of records from application layer to the client.In both cases the data brought to the client side would be the same but in direct linq case there will be time lapse in bringing huge data from Database Layer to application layer.So you can also use this second i.e linq approach if your application and DB both are running on the same server.But anyways i prefer the first modifying the stored proc approach because it is the best.
  • @pageSize is the total no of records you want to be displayed per page.
  • @sortColumnName is the name of the actual column which you want to sort.From the UI  we will pass this name by catching which column was actually clicked.
  • @sortDirection is the SortDirection either ASC or DESC
  • @filterStatusId this is my custom implementation which is very specific you can extend it very easily,i couldn't get time to implement this may be one of you can implement it and share the code here.
  • @advertiserAccountId this is a custom parameter which is passed to retrieve the campaigns related to an advertiser.
  • @totalRecords this is an output type parameter which always returns the total no of records which are required for paging.
DECLARE @campaigns TABLE
(CampaignID int,row_num int);

This declares a temporary table which will hold all the actual sorted and filtered records which we will join with the original table and implement paging by providing the pageSize and pageCount logic and retrieving only required amount of records.

 

INSERT INTO @campaigns
	SELECT CampaignID,ROW_NUMBER() OVER(
	ORDER BY
	CASE WHEN @sortDirection = 1 THEN NULL			
			WHEN @sortColumnName = 'CampaignName' THEN CONVERT(VARCHAR(200),CampaignName)
			WHEN @sortColumnName = 'CampaignStatusName' THEN CONVERT(VARCHAR(100),CampaignStatusName)
			WHEN @sortColumnName = 'UtcStartDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			WHEN @sortColumnName = 'UtcEndDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			
			ELSE NULL END DESC,
			CASE WHEN @sortDirection <> 1 THEN NULL
			WHEN @sortColumnName = 'CampaignName' THEN CONVERT(VARCHAR(200),CampaignName)
			WHEN @sortColumnName = 'CampaignStatusName' THEN CONVERT(VARCHAR(100),CampaignStatusName)
			WHEN @sortColumnName = 'UtcStartDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			WHEN @sortColumnName = 'UtcEndDate' THEN CONVERT(VARCHAR(100),UtcStartDate)
			ELSE NULL END ASC			
	) AS [row_num] FROM dbo.CampaignDetailsView c WHERE c.AdvertiserAccountID = @advertiserAccId

2. This includes selection of all records in temporary table based on sorting inside the order by clause.Here we are passing sortDirection based on which the records are sorted either ascending or descending.This is the most important part of this stored proc.

SET @totalRecords = @@ROWCOUNT
	SELECT c.* FROM dbo.CampaignDetailsView AS c JOIN @campaigns AS tc ON c.CampaignID = tc.CampaignID
	WHERE tc.row_num BETWEEN (((@PageNum - 1) * @PageSize) + 1) 
AND (@PageNum * @PageSize)
ORDER BY tc.row_num

Return @totalRecords

This is the main step where paging is actually implemented.Its very simple join of temp and actual table and then retrieving the pageSize amount of records between the currentPage and the nextPage

AS [row_num] FROM dbo.CampaignDetailsView c WHERE c.AdvertiserAccountID = @advertiserAccId AND c.CampaignStatusID = @filterStatusId

3. Filtering is pretty straight forward you can just get only the records by passing a parameter here we are filtering records on the filterStatusId passed from the UI

Filtering is straight forward but sorting is little typical because you cannot write like this Order by @passedparameter @passedSortDirection.This will result in errors.

4. Actually my business object creation required two objects according to my model you can very well make changes according to your requirements.I have created two BO one having simply the private members and properties of the CampaignDetails and also named as CampaignDetails and other is a complex object although merely simple, it just holds total records which are passed by our stored proc and the list of records passed by our stored proc

public class CampaignDetails
    {
        private int _CampaignID;

        private string _CampaignName;

        private int _AdvertiserAccountID;

        private System.Nullable<int> _BrandProfileID;

        private int _AudienceProfileID;

        private decimal _BudgetAmount;

        private System.DateTime _UtcStartDate;

        private System.DateTime _UtcEndDate;

        private int _CampaignStatusID;

        private int _BuyTypeID;

        private int _PacingTypeID;

        private System.DateTime _CreatedDateTime;

        private string _AudienceProfileName;

        private string _BrandProfilename;

        private string _BrandName;

        private int _BusinessCategoryID;

        private string _BusinessCategoryName;

        private string _CampaignStatusDescription;

        private string _CampaignStatusName;

        private string _SalesChannelName;

        private System.Nullable<int> _GenderID;

        [Column(Storage = "_CampaignID", DbType = "Int NOT NULL")]
        public int CampaignID
        {
            get
            {
                return this._CampaignID;
            }
            set
            {
                if ((this._CampaignID != value))
                {
                    this._CampaignID = value;
                }
            }
        }

        [Column(Storage = "_CampaignName", DbType = "NVarChar(200) NOT NULL", CanBeNull = false)]
        public string CampaignName
        {
            get
            {
                return this._CampaignName;
            }
            set
            {
                if ((this._CampaignName != value))
                {
                    this._CampaignName = value;
                }
            }
        }

        [Column(Storage = "_AdvertiserAccountID", DbType = "Int NOT NULL")]
        public int AdvertiserAccountID
        {
            get
            {
                return this._AdvertiserAccountID;
            }
            set
            {
                if ((this._AdvertiserAccountID != value))
                {
                    this._AdvertiserAccountID = value;
                }
            }
        }

        [Column(Storage = "_BrandProfileID", DbType = "Int")]
        public System.Nullable<int> BrandProfileID
        {
            get
            {
                return this._BrandProfileID;
            }
            set
            {
                if ((this._BrandProfileID != value))
                {
                    this._BrandProfileID = value;
                }
            }
        }

        [Column(Storage = "_AudienceProfileID", DbType = "Int NOT NULL")]
        public int AudienceProfileID
        {
            get
            {
                return this._AudienceProfileID;
            }
            set
            {
                if ((this._AudienceProfileID != value))
                {
                    this._AudienceProfileID = value;
                }
            }
        }

        [Column(Storage = "_BudgetAmount", DbType = "Money NOT NULL")]
        public decimal BudgetAmount
        {
            get
            {
                return this._BudgetAmount;
            }
            set
            {
                if ((this._BudgetAmount != value))
                {
                    this._BudgetAmount = value;
                }
            }
        }

        [Column(Storage = "_UtcStartDate", DbType = "DateTime NOT NULL")]
        public System.DateTime UtcStartDate
        {
            get
            {
                return this._UtcStartDate;
            }
            set
            {
                if ((this._UtcStartDate != value))
                {
                    this._UtcStartDate = value;
                }
            }
        }

        [Column(Storage = "_UtcEndDate", DbType = "DateTime NOT NULL")]
        public System.DateTime UtcEndDate
        {
            get
            {
                return this._UtcEndDate;
            }
            set
            {
                if ((this._UtcEndDate != value))
                {
                    this._UtcEndDate = value;
                }
            }
        }

        [Column(Storage = "_CampaignStatusID", DbType = "Int NOT NULL")]
        public int CampaignStatusID
        {
            get
            {
                return this._CampaignStatusID;
            }
            set
            {
                if ((this._CampaignStatusID != value))
                {
                    this._CampaignStatusID = value;
                }
            }
        }

        [Column(Storage = "_BuyTypeID", DbType = "Int NOT NULL")]
        public int BuyTypeID
        {
            get
            {
                return this._BuyTypeID;
            }
            set
            {
                if ((this._BuyTypeID != value))
                {
                    this._BuyTypeID = value;
                }
            }
        }

        [Column(Storage = "_PacingTypeID", DbType = "Int NOT NULL")]
        public int PacingTypeID
        {
            get
            {
                return this._PacingTypeID;
            }
            set
            {
                if ((this._PacingTypeID != value))
                {
                    this._PacingTypeID = value;
                }
            }
        }

        [Column(Storage = "_CreatedDateTime", DbType = "DateTime NOT NULL")]
        public System.DateTime CreatedDateTime
        {
            get
            {
                return this._CreatedDateTime;
            }
            set
            {
                if ((this._CreatedDateTime != value))
                {
                    this._CreatedDateTime = value;
                }
            }
        }

        [Column(Storage = "_AudienceProfileName", DbType = "NVarChar(200) NOT NULL", CanBeNull = false)]
        public string AudienceProfileName
        {
            get
            {
                return this._AudienceProfileName;
            }
            set
            {
                if ((this._AudienceProfileName != value))
                {
                    this._AudienceProfileName = value;
                }
            }
        }

        [Column(Storage = "_BrandProfilename", DbType = "NVarChar(200) NOT NULL", CanBeNull = false)]
        public string BrandProfilename
        {
            get
            {
                return this._BrandProfilename;
            }
            set
            {
                if ((this._BrandProfilename != value))
                {
                    this._BrandProfilename = value;
                }
            }
        }

        [Column(Storage = "_BrandName", DbType = "NVarChar(200) NOT NULL", CanBeNull = false)]
        public string BrandName
        {
            get
            {
                return this._BrandName;
            }
            set
            {
                if ((this._BrandName != value))
                {
                    this._BrandName = value;
                }
            }
        }

        [Column(Storage = "_BusinessCategoryID", DbType = "Int NOT NULL")]
        public int BusinessCategoryID
        {
            get
            {
                return this._BusinessCategoryID;
            }
            set
            {
                if ((this._BusinessCategoryID != value))
                {
                    this._BusinessCategoryID = value;
                }
            }
        }

        [Column(Storage = "_BusinessCategoryName", DbType = "NVarChar(200)")]
        public string BusinessCategoryName
        {
            get
            {
                return this._BusinessCategoryName;
            }
            set
            {
                if ((this._BusinessCategoryName != value))
                {
                    this._BusinessCategoryName = value;
                }
            }
        }

        [Column(Storage = "_CampaignStatusDescription", DbType = "NVarChar(500)")]
        public string CampaignStatusDescription
        {
            get
            {
                return this._CampaignStatusDescription;
            }
            set
            {
                if ((this._CampaignStatusDescription != value))
                {
                    this._CampaignStatusDescription = value;
                }
            }
        }

        [Column(Storage = "_CampaignStatusName", DbType = "NVarChar(50) NOT NULL", CanBeNull = false)]
        public string CampaignStatusName
        {
            get
            {
                return this._CampaignStatusName;
            }
            set
            {
                if ((this._CampaignStatusName != value))
                {
                    this._CampaignStatusName = value;
                }
            }
        }

        [Column(Storage = "_SalesChannelName", DbType = "NVarChar(200) NOT NULL", CanBeNull = false)]
        public string SalesChannelName
        {
            get
            {
                return this._SalesChannelName;
            }
            set
            {
                if ((this._SalesChannelName != value))
                {
                    this._SalesChannelName = value;
                }
            }
        }

        [Column(Storage = "_GenderID", DbType = "Int")]
        public System.Nullable<int> GenderID
        {
            get
            {
                return this._GenderID;
            }
            set
            {
                if ((this._GenderID != value))
                {
                    this._GenderID = value;
                }
            }
        }
    }

Now the other object holding the list of CampaignDetails and total records is CampaignDetailsPaged

public class CampaignDetailsPaged
    {
        private List<CampaignDetails> campaignDetailsAdvertiser;

        public int TotalRecords { get; set; }

        public List<CampaignDetails> CampaignDetailsAdvertiser
        {
            get
            {
                if(campaignDetailsAdvertiser == null)
                {
                    campaignDetailsAdvertiser = new List<CampaignDetails>();
                }
                return campaignDetailsAdvertiser;
            }
        }

    }

5. Creating the DAO Layer function

This is fairly simple, if you are lazy just drag and drop your stored proc in your dbml file and see the designer.cs file for the code.Actually here i am having different data context and i m not using the Linq dbml file at all , i only mentioned it to just help in easing and creating the required code for the stored proc.

[Function(Name = "dbo.GetCampaignByAdvId")]
        public ISingleResult<CampaignDetails> GetCampaignByAdvId([Parameter(DbType = "Int")] System.Nullable<int> pageNum, [Parameter(DbType = "Int")] System.Nullable<int> pageSize, [Parameter(DbType = "VarChar(200)")] string sortColumnName, [Parameter(DbType = "Int")] System.Nullable<int> sortDirection, [Parameter(DbType = "Int")] System.Nullable<int> filterStatusId, [Parameter(DbType = "Int")] System.Nullable<int> advertiserAccId, [Parameter(DbType = "Int")] ref System.Nullable<int> totalRecords)
        {
            IExecuteResult result = this.ExecuteMethodCall(this, ((MethodInfo)(MethodInfo.GetCurrentMethod())), pageNum, pageSize, sortColumnName, sortDirection, filterStatusId, advertiserAccId, totalRecords);
            totalRecords = ((System.Nullable<int>)(result.GetParameterValue(6)));
            return ((ISingleResult<CampaignDetails>)(result.ReturnValue));
        }

6. Creating the Service Layer function

Actually speaking we already have completed all the difficult parts and now very simple things are left, now we are just connecting the dots and nothing else

public CampaignDetailsPaged GetCampaignsByAdvertiserId(int pageNum, int pageSize, int advertiserAccId, string sortColumnName,
 int sortDirection, int filterStatusId)
    {
        AdBuyerDataContext adBuyerDataContext = new AdBuyerDataContext(dbConnString);
        int? totalRecords = 0;
        CampaignDetailsPaged campaignDetailsPaged = new CampaignDetailsPaged();
        var ret = adBuyerDataContext.GetCampaignByAdvId(pageNum, pageSize, sortColumnName, sortDirection, filterStatusId,
            advertiserAccId, ref totalRecords);
        List<CampaignDetails> campaignDetails = ret.ToList<CampaignDetails>();
        campaignDetailsPaged.TotalRecords = totalRecords.Value;
        if (campaignDetails.Count > 0)
        {
            foreach (CampaignDetails campaigns in campaignDetails)
            {
                campaignDetailsPaged.CampaignDetailsAdvertiser.Add(campaigns);
            }
        }
        return campaignDetailsPaged;
    }

Here totalrecords returned from the stored proc out parameter are assigned to the TotalRecords property of CampaignDetailsPaged BO and List of campaignDetails is also generated and BO CampaignDetails is returned

7. Calling this Service Layer Function from our Page

Calling this service layer function from the page is also pretty straightforward you just have to include a reference in your code behind file to System.Web.Service and write your code as given below

[WebMethod]
   public static CampaignDetailsPaged GetCampaignsForAdvertiser(string currPage, string pageSize, string advertiserAccId,
       string sortColumnName, int sortDirection, int filterStatusId)
   {
       YourService yourService = new YourService();
       int currentPage = Int32.Parse(currPage);
       int pagesize = Int32.Parse(pageSize);
       int advertiserid = Int32.Parse(advertiserAccId);
       return yourService.GetCampaignsByAdvertiserId(currentPage, pagesize, advertiserid, sortColumnName, sortDirection,
           filterStatusId);

   }

Here we are passing all the required parameters from the UI.One disadvantage of using WebMethods is that the method needs to be static so you cannot access non-static members of the page in this static web method that means you cannot access any server side controls on the page inside this static method, but in our implementation we are not using any server side controls that's why we can use WebMethods

The better approach would have been to use WCF Restful services which can return result in both XML and JSON formats.

I am using WebMethods for particular reason that there  are not so many clear examples available for the WebMethod to be used with jQuery,JSON

Another reason is in your specific implementation your client does not want to use the WebService way then these WebMethods can come handy.

8. Calling this WebMethod from built in jQuery ajax functions to get a JSON response from the server.

function ClientProxy(methodName, paramArray, successFunction, errorFunction) {
    var pagePath = window.location.pathname;
    var paramList = '';

    if (paramArray.length > 0) {
        for (var i = 0; i < paramArray.length; i += 2) {
            if (paramList.length > 0) paramList += ',';
            paramList += '"' + paramArray[i] + '"' + ':' + '"' + paramArray[i + 1] + '"';
        }
    }
    paramList = '{' + paramList + '}';

    $.ajax({
        type: "POST",
        url: pagePath + "/" + methodName,
        contentType: "application/json; charset=utf-8",
        data: paramList,
        dataType: "json",
        success: function(msg) {
            successFunction(msg);
        },
        error: function() {
            errorFunction();
        }
    });
    return false;
}

I have wrapped the $.ajax() jQuery function for Generalizing it to be used with any method and you need not to duplicate it every time.

In this method i receive the

methodName i.e name of WebMethod to be called,

paramArray i.e an array having key value pairs in form of JSON string,

successFunction i.e the function to be called on successful completion of the ajax call

errorFunction i.e the function to be called when an error occurs while retrieving the results
9. Plugging in your jTemplate or any other templating engine

jTemplate is a really very cool and interesting templating engine available out there only problem with this is that its python style syntax may confuse you.But i found it really very powerful.

If you are not using jQuery and using MicorosoftAjax then you can also give try to inbuilt templating features in the new release of Microsoft Ajax Framework

Bertrand Le Roy's has a very good entry explaining Microsoft Ajax templating here :-

http://weblogs.asp.net/bleroy/archive/2009/02/05/how-to-choose-a-client-template-engine.aspx

I have personally chose jTemplates as i was using jQuery and i was not using Microsoft Ajax at all in my approach.But ups and downs are always there,anyways you can choose anything.But if you are following a approach in which you don't use Microsoft Ajax Framework then jTemplates is the best approach to go.

Also you can choose John Resig's micro template they are also a better alternative small and robust and can be found here

http://ejohn.org/blog/javascript-micro-templating/

Major Benefits of using templates is that you can have full control over your rendered HTML and you call only data on the page not the html from the server.

This actually is the main drawback of using the update panel as such in your applications update panel by default brings all your markup as well as data again from the server.So if someone out there is using Update Panels be careful while putting the controls and other content inside the update panel.

Coming to our case we will receive the pure JSON response as

There is a cool way of inserting templates inside your markup, you can achieve this by using

<script type="text/html">

Call the ClientProxy Function to call the webmethod as we have already discussed and pass the required parameters

function LoadCampaign() {
                $("#divProcessing").insertAfter("#content").show();
                ClientProxy("GetCampaignsForAdvertiser", ["currPage", currCampaignPage, "pageSize", numPerPage, "advertiserAccId", advertiserAccId, "sortColumnName", sortColumnName, "sortDirection", sortDirection, "filterStatusId", filterId],
            function(msg) { ApplyTemplateCampaign(msg); }, function() { errorApplyTemplate(); });
            }

Now to implement paging you need a little code

LoadPaging actually initializes the the paging functionality and here we set the pageCount and call the setCampaignPaging() function which checks if the currentPage is the first page or the last page this is to disable and enable the previous and next buttons.

CampaignNextPage and CampaignPreviousPage are used to navigate to previous and next pages in the list.

function loadCampaignPaging(totalRecords) {
           campaignPageCount = Math.ceil(totalRecords / numPerPage);
           setCampaignPaging();
       }
       function setCampaignPaging() {
           if (currCampaignPage === 1) {
               $('#prevCampaign').attr('disabled', 'disabled');
           } else {
               $('#prevCampaign').attr('disabled', '');
               $('#prevCampaign').click(CampaignPrevPage);
           }

           if (currCampaignPage === campaignPageCount) {
               $('#nextCampaign').attr('disabled', 'disabled');
           } else {
               $('#nextCampaign').attr('disabled', '');
               $('#nextCampaign').click(CampaignNextPage);
           }
       }

       function CampaignNextPage() {
           currCampaignPage = currCampaignPage + 1;
           ClientProxyDateFormatted("GetCampaignsForAdvertiser", ["currPage", currCampaignPage, "pageSize", numPerPage, "advertiserAccId", advertiserAccId, "sortColumnName", sortColumnName, "sortDirection", sortDirection, "filterStatusId", filterId],
           function(msg) { ApplyTemplateCampaign(msg); }, function() { errorApplyTemplate(); });
           setCampaignPaging();

       }

       function CampaignPrevPage() {
           currCampaignPage = currCampaignPage - 1;
           ClientProxyDateFormatted("GetCampaignsForAdvertiser", ["currPage", currCampaignPage, "pageSize", numPerPage, "advertiserAccId", advertiserAccId, "sortColumnName", sortColumnName, "sortDirection", sortDirection, "filterStatusId", filterId],
           function(msg) { ApplyTemplateCampaign(msg); }, function() { errorApplyTemplate(); });
           setCampaignPaging();
       }

ApplyTemplateCampaign(msg) by doing this we are calling a function ApplyTemplateCampaign(msg) and in this function we are passing the results received from the server.

Actually ApplyTemplateCampaign function calls the SetTemplate and processTemplate methods which actually fill and load data from the page to the actual UI

function ApplyTemplateCampaign(msg) {
            var totalRecords = msg.d.TotalRecords;
            loadCampaignPaging(totalRecords);
            $("#campaignDetailsContent").setTemplate($("#CampaignDetailsAdvertiser").html(),
            null, { filter_data: false });
            $("#campaignDetailsContent").processTemplate(msg);

#campaignDetailsContent is the name of the actual empty div on which the template will be applied and #CampaignDetailsAdvertiser is the id of the template which will be applied

 <script id="CampaignDetailsAdvertiser" type="text/html">
    <table id="campaignDetailsTable">
    <thead>
        <tr>
            <th>
               Name
            </th>
            <th>
                Status
            </th>
            <th>
                Budget Used
            </th>
            <th>
            Budget Remaining
            </th>
            <th>
            Impressions Goal
            </th>
            <th>
            Impressions Delivered
            </th>
            <th>
            Clicks Delivered
            </th>
            <th>
            CTR(clicks/imps)
            </th>
            <th>
            Start Date
            </th>
            <th>
            End Date
            </th>
        </tr>
    </thead>
    <tbody>
        {#foreach $T.d.CampaignDetailsAdvertiser as campaign}
        <tr>
            <td class="id">
                <a class="lnkCampaignName" href="CampaignDetails.aspx?campaignId={$T.campaign.CampaignID}">{$T.campaign.CampaignName}</a>
                <div class="infoContainerCampaign">
                    <div class="shoutContainerCampaign">
                    </div>
                    <div class="infoContentCampaign">
                        <ul>
                        <li>
                        <h2>{$T.campaign.CampaignName}</h2>
                        <hr/>
                        </li>
                        <li><span class="rollOverHeading" >Budget :</span>{$T.campaign.BudgetAmount}</li>
                        <li><span class="rollOverHeading" >Duration :</span>{$T.campaign.UtcStartDate}-
                        {$T.campaign.UtcEndDate}</li>
                        <br/>
                        <li><span class="rollOverHeading" >Brand Profile :</span>{$T.campaign.BrandProfilename}</li>
                        <li><span class="rollOverHeading" >Name :</span>{$T.campaign.BrandProfilename}</li>
                        <li><span class="rollOverHeading" >Category :</span>{$T.campaign.BrandProfilename}</li>
                        <li><span class="rollOverHeading" >Channel :</span>{$T.campaign.BrandProfilename}</li>
                        <br/>
                        <li><span class="rollOverHeading" >Audience Profile :</span>{$T.campaign.AudienceProfileName}</li>
                        <li><span class="rollOverHeading" >Gender :</span>{$T.campaign.BrandProfilename}</li>
                        <li><span class="rollOverHeading" >Age Group :</span>{$T.campaign.BrandProfilename}</li>
                        <li><span class="rollOverHeading" >Location :</span>{$T.campaign.BrandProfilename}</li>
                        <br/>
                        <li><span class="rollOverHeading" >Status :</span>{$T.campaign.CampaignStatusName}</li>
                    </div>
                </div>
            </td>
            <td>
                {$T.campaign.CampaignStatusName}
            </td>
            <td>
            </td>
            <td>
            </td>
            <td>
            </td>
            <td>
            </td>
            <td>
            </td>
            <td>
            </td>
            <td>
            {$T.campaign.UtcStartDate}
            </td>
            <td>
            {$T.campaign.UtcEndDate}
            </td>
        </tr>
        {#/for}
    </tbody>
</table>

    </script>

Given above is the actual implementation of the template which will be rendered inside the div.In above code i have shown how to read the complex object returned from JSON.

10.  That's all what you need to do while implementing your own client side grid with paging,sorting and filtering functionality.

In Progress.........

  • Live Demo Page for the implementations shown here - i am working on a live demo page for the implementations shown here.
  • Source Code with whole solution and all the layers - i am working on making whole solution to be available as download
  • More generic form of this control which i will make once i get time.
  • Advanced filters and multi column sorting
  • Making the NextPage and PrevPage Functions more generic to accept the function name to be called and then calling that function only(if any of you implement this plz share here), i will do all these but it may take some time for me as i m very busy with my schedule.

If any of you have time and implemented the functionalities do share here as it will help many more people.

Stay tuned for more Action..........

Happy Programming!!!!!!!!!!!!!!!!!!!!!!!!!

Update:-

You can have a look at the working demo at http://www.effectlabs.com/projects/jqnetgrid/


Jquery Series Part 1:- Basics & Introduction

    1. Jquery is a JavaScript library which makes life with JavaScript and DOM easier and common tasks really simple and trivial
    2. $("table tr:nth-child("even").addClass("striped");
    3. Selectors are the most powerful feature of Jquery.The raw power of Jquery is in selecting and grabbing an element and then playing with that elements style or its surrounding elements.
    4. The concept of Unobtrusive Java Script tells us that any JavaScript code placed inside the <body> tag  is incorrect so this means that we have to separate our behavior from our markup or ui
    5. Without using the concept of unobtrusive JavaScript we can say these lines of code to be correct
      1. <input type="button" onclick="document.getElementById('testDiv').style.color = 'red';" />
    6. Now if we want to use the concept of unobtrusive java script here and want to separate the behavior and markup then we can simply use
<input type="button" id="testButton" />
Now we can have this script in the header section thereby separating the behaviour from the markup
window.onload = function (){
document.getElementById('testButton').onClick = changeColor;
};
function changeColor()
{
}
  1. But ultimately what has happened is that we have increased no of lines of code in lust to separate but as far as good programming practices are considered on the client side then this approach is the best although it increases no of lines of code.
  2. So what jQuery does is simplify this simple unobtrusive JavaScript concept using few lines of code.
  3. It also has various other magic features which we will discuss in other articles of this series such as chaining
  4. Jquery handles most of the browser issues by itself which is a major advantage of using jQuery over raw JavaScript
  5. Jquery waits until the page is loaded so that we have all the controls created before any call or action to them this is another added advantage.
  6. Jquery is fully extensible that's what millions of developers are doing out there using jquery.
  7. Jquery is so extensible, one reason for embedding so much extensibility is its light weight it's just 15-17 KB of download depending on your version on the client system with minimal still powerful set of functionality.If you are not satisfied and want more then you are open to extend it or use already available rich set of plugins and other third party tools available for jquery.
  8. $() is the wrapper used to wrap jQuery() what $ does  is it selects the set of elements and return a JavaScript object containing array of all DOM elements that match the selector
  9. Another important power of Jquery that it returns the set of all objects which were initially selected upon performing one operation on those set of elements ie if i select all the p elements of div's with class = "dance" and perform a single line function $('dance').fadeOut(); it will first fadeout all the divs and p's with the required class dance and after fading out it will again return all these objects for further performing some actions on these set of objects.
  10. So actually we can chain these elements a thousand layer deep all in a single line of code as shown
    1. $('dance').fadeOut().addClass("dancing"); that's all after performing fadeout now the objects will follow what is written in dancing css class and this will fire only when fadeout is completed not along with fadeOut that a real power with jQuery Chaining
  11. There are various so called commands in jquery such as html and others which will directly allow you to play with the selected DOM elements
    1. $('#someElement').html("This text is added afterwards");
  12. This was one example of selecting but jQuery is much more advanced in selecting then any of my articles or sessions can convey to you its real power can be leveraged only by you all creative minds out there, i can just introduce it to you but afterall its your creativity of playing with the DOM which will lead to ultimate cool things to inspire others.
  13. Some examples i have prepared for you of much advanced selectors remembers these selectors though seeming advanced to you are nothing for jQuery it can handle much more advanced selectors.
    1. $("p:even");

      This selector selects all even <p> elements.$("tr:nth-child(1)");

      This selector selects the first row of each table.

      $("body > div");

      This selector selects direct <div> children of <body>.

      $("a[href$=pdf]");

      This selector selects links to PDF files.

      $("body > div:has(a)" this selector selects direct div of the body tag which have a elements that the links.

  14.    Also you can check one of my other post on using regular expression syntax with jquery to select group of elements having common word in their id's.http://localhost/smallworkarounds/index.php/2009/01/04/jquery-and-regular-expressionstrick-1/
  15. I will be doing more basic,intermediate and advanced level tutorials on Jquery, Microsoft Ajax Library and combination of both in order to get maximum output and using Client Side Ajax Control Templates and Data View Controls to maximize for scalability.

 

Stay tuned for more on Jquery!!!!!!!!!!!

Happy Programming !!!!!!!!


Jquery And Regular Expressions,Trick #1:- Selecting all the tags matching some string or having a particular string in its id or classname

This trick will come very handy for the server controls which sometimes emit their own id's while rendering the equivalent HTML.Most of the server controls append the id of their parent server control container. So while working with asp.net it becomes a tedious task to catch all those id's which are different from their actual id's which we gave using the 'id' attribute. This is one of the ugliest thing of WebForms and also this can be treated as a disadvantage while using webforms because we donot have direct id's as given while we were writing the code, i.e what we gave is not what we recieve. And this is one of the advantage of using MVC framework as it gives more control on the rendered HTML. So here we assume a situation in where we are not using MVC, we are only using webforms and in our case there is a situation where in we have widgets which are user controls and are created dynamically as soon as they are dropped.These widgets when rendered are enclosed inside a div having an id="widget123333444asdfdf" i.e in the id there is a word widget which is fixed and is inherited and rendered by every widget but after the widget word another random guid value is appended which probably might be the id of widget. So in this case if we only have id's and no class is assigned to these widgets and if we want to give some common styling to these widgets then it becomes a problem to select all dynamically created widgets and perform some operations on them. You may argue why not use a common class for them and then style them or do whatever you want.But believe me my friends i am telling with experience that there are situations when you only will have id's or any one selector and you can't even edit the markup you have to do stuff's with only what you have. So considering that situation there is a simple one line hack using Jquery Regular expression attribute selectors. These attribute selectors can be used in different ways.I will do detailed posts on these selectors in future. But for now lets concentrate on our particular condition and get a way to select all the elements having a common word in their id's Consider the example below:-

<body>
    <form id="form1" runat="server">
    <div>
        <div id="first1">
            This is the first div
        </div>
        <div id="first2">
            This is the second div
        </div>
        <div id="first3">
            This is the third div
        </div>
        <input type="button" class="changecolor" value="ChangeColor" />
    </div>
    </form>
</body>

Here we try to mimic the situation discussed above by creating 3 div's having "first" as common in their id's followed by their repective numbers.

Now we will use the jquery attribute selector with simple regular expression to selecte each of these 3 div's and on click of the changecolor button the background color of each div should be changed this is what we have done below.

<style>
        .changecolor
        {
            border: none;
            background-color: #999999;
            color: #fff;
            margin-top: 10px;
            cursor: pointer;
        }
    </style>

    <script src="http://www.google.com/jsapi"></script>

    <script type="text/javascript">
        google.load("jquery", "1.2.6");
        google.setOnLoadCallback(function() {
            $('#first1').css({ 'background-color': '#C3E5A7' });
            $('#first2').css({ 'background-color': '#A7C4E5' });
            $('#first3').css({ 'background-color': '#DAA7E5' });
            $('.changecolor').click(function() {
                $('div[id*=first]').css({ 'background-color': '#E5C6A7' });
            });
        }); 
    </script>

So here the last line is important in which we use $('div[id*=first]')..... This selector will select all the div's having "first" in their id's at any place.

So this gives us a very simple solution for a so called complex problem.

We can use regex selectors with attributes in many different ways which i will show with examples in future posts so stay tuned....

Happy Programming!!!....

You can view live demo of this example here:-

RegexSelector Live Demo


JQuery with Asp.net and Visual Studio 2008 intellisense

Here is a small post on Jquery which is a far better alternative to Aspnet Ajax.Its a small JavaScript library with immense power and most widely used these days.A few months ago Microsoft announced that they will be shipping JQuery with Next Release of their Visual Studio i.e Visual Studio 2010. Visual Studio 2010 and .net framework 4.0 Community Technology Preview are already available  for download from http://www.microsoft.com/downloads/details.aspx?FamilyId=922B4655-93D0-4476-BDA4-94CF5F8D4814&displaylang=en I am not sure about the intellisense support for JQuery and JavaScript in this new version but as Microsoft Promised they will surely ship it in their final release. As for now with Visual Studio 2008 full intellisense for JQuery is achieved using a .vsdoc.js file.   Steps to follow to get full intellisense

  1. Install this Hotfix which will fix many issues for Visual Studio Team Systems 2008 and also enable and enhance you script intellisense if somehow it is disabled or its not coming with many other fixes. Download Link :-https://connect.microsoft.com/VisualStudio/Downloads/DownloadDetails.aspx?DownloadID=10826
  2. Jeff King has a very good post describing the second step here.

http://blogs.msdn.com/webdevtools/archive/2008/10/28/rich-intellisense-for-jquery.aspx

Here a hack is applied so there two files are given one is "jquery-1.2.6.js" another is "jquery-1.2.6-vsdoc.js".So the first file is the actual Jquery file which should be downloaded on the clients browser along with the markup whereas second file should only be available while using intellisense inside the Visual Studio 2008.The markup comments shown above are achieving the same thing. A very simple Visual Studio Solution is attached at the end of this post for you to see the architecture of how to include jquery files and how to do very basic things using Jquery in an aspx page having basic markup as HTML.

After following these steps you are now ready to start Jquery development in Asp.net Download Source Code Here :-http://9wtpog.bay.livefilestore.com/y1pPZ0KF1m2F28wRGe4wQYrbDCyIuWruESEpdlcILrP_8rIkJfe2eTchl6iw6KSUMwRroVAQ2UvoCo/JqueryIntellisenseInstallation.rar?download