checklist with red checks and male hand pointing at one check

Create a Simple Task List Using ASP.NET, WCF, and jQuery, Part 1

Easily make an application that allows users to define, add, edit, delete, and filter tasks

Maintaining a task list (or a to-do list) is a common time management technique that helps us keep track of work and agenda items, along with their priority and status. No wonder Windows Task Manager is often an integral part of web and desktop calendaring applications. In this two-part article, we will build our own task-list application using jQuery, ASP.NET, and Windows Communication Foundation (WCF). Though simple, our task-list application will illustrate how jQuery can interact with server data through WCF services. We will also get to apply several jQuery concepts such as selectors, AJAX calls, and plug-ins.

Functional Requirements

First, let’s summarize our expectations for the task-list application that we are going to build:

  • Users should be able to define tasks. A task definition consists of title, description, priority (e.g., high, normal, low); status (e.g., pending, in-process, completed); and due date.
  • Users should be able to add, edit, and delete tasks.
  • Users should be able to filter tasks on the basis of priority and status.
  • Tasks that have a due date that has already passed should be highlighted when the application starts.
  • The application should talk with the server only during add, edit, and delete operations to keep post backs or page refreshes to a minimum.

We will use jQuery, ASP.NET 4.0, and SQL Server 2008 to develop the task-list application. The application will illustrate how to:

  • Use common jQuery selectors.
  • Use Microsoft's jQuery template plug-in to create a grid layout.
  • Deal with dates while communicating with WCF.
  • Make AJAX calls to WCF service through jQuery.
  • Create your own plug-in using jQuery.

Creating the Web Application

To begin, use Visual Studio to create a new ASP.NET website. Figure 1 shows the New Web Site dialog box in Visual Studio.

Figure 1: Creating a new website in Visual Studio
Figure 1: Creating a new website in Visual Studio

Once the website is created, add a new folder named Scripts, and place the jQuery files in it, as Figure 2 illustrates.

Figure 2: Placing jQuery script files
Figure 2: Placing jQuery script files

Note: For detailed instructions about downloading the jQuery library and the jQuery templates plug-in, see the ReadMe.txt file in the code download that accompanies this article.

All the data that the task-list application requires will be stored in a SQL Server database. To add a new SQL Server database to your website, right-click the App_Data folder and select Add New Item to open a dialog box, which Figure 3 shows.

Figure 3 : Adding a SQL Server database
Figure 3 : Adding a SQL Server database

Give the database a name, such as TaskListDb.mdf. Once the database is ready, create a new table named Tasks with the schema as shown in Figure 4.

Figure 4: Tasks table schema
Figure 4: Tasks table schema

The Tasks table has six columns: TaskID, Title, Description, Priority, DueDate, and Status.

Creating a WCF Service to Perform Data Access

The data from the Tasks table needs to be accessed from the client side using jQuery code. Hence, we need a callable endpoint at the server end that can be consumed from the client. A WCF service can provide such an endpoint. We will use a WCF service for adding, updating, deleting, and retrieving data from the Tasks table. Later, we will call this WCF service from the jQuery code.

To create a WCF service, open the Add New Item dialog box again and select WCF Service, as shown in Figure 5.

Figure 5: Adding a new WCF service
Figure 5: Adding a new WCF service

Adding a new WCF service will create a .svc file under the root folder and a .cs file under the App_Code folder. The code file contains an interface (IService) that defines the service contract. We will modify the IService interface to include Insert, Update, Delete, and SelectAll methods, as shownin Figure 6.

[ServiceContract]
public interface IService   
{   
  [OperationContract]   
  [WebInvoke(Method = "POST", 
   RequestFormat = WebMessageFormat.Json, 
   ResponseFormat = WebMessageFormat.Json)]
  int Insert(TaskData t);
   
  [OperationContract] 
  [WebInvoke(Method = "POST", 
   RequestFormat = WebMessageFormat.Json, 
   ResponseFormat = WebMessageFormat.Json)]
  int Update(TaskData t);

  [OperationContract]
  [WebInvoke(Method = "POST", 
   RequestFormat = WebMessageFormat.Json, 
   ResponseFormat = WebMessageFormat.Json)]
  int Delete(Guid id);

  [OperationContract] 
  [WebInvoke(Method = "POST", 
   RequestFormat = WebMessageFormat.Json, 
   ResponseFormat = WebMessageFormat.Json)]
  TaskData[] SelectAll();
}

Notice that the IService interface is decorated with the ServiceContract attribute, indicating that it is a WCF service contract. All the methods of the IService interface are marked with two attributes, namely OperationContract and WebInvoke. The former attribute indicates that the method is a WCF callable operation, and the latter indicates that the method can be called via the Representational State Transfer (REST) programming model. The WebInvoke attribute also specifies three properties. The Method property specifies the HTTP method (GET or POST) that will invoke the method under consideration. The RequestFormat and ResponseFormat properties govern the format in which the request data and response data are serialized. The value of Json indicates that the data transfer between client and server will happen using the JavaScript Object Notation (JSON) format. Another possibility is XML, but since we are using jQuery on the client side, we will stick to the JSON format.

The methods as defined by the following IService interface will be implemented by the Service class, and these are discussed later.

public class Service : IService
{
  …
}

Implementing the Service Contract

Before we go into the details of implementing the IService interface, let's create LINQ to SQL classes that will help us perform database operations, such as INSERT, UPDATE, DELETE and SELECT. Open the Add New Item dialog box again, as shown in Figure 7. This time, select the LINQ to SQL Classes option.

Figure 7: Adding LINQ to SQL classes
Figure 7: Adding LINQ to SQL classes

This will add a .dbml file under App_Code folder. Drag and drop the Tasks table from the Server Explorer onto the design surface of the .dbml file to create a LINQ to SQL class, as shown in Figure 8.

Figure 8: LINQ to SQL class for Task table
Figure 8: LINQ to SQL class for Task table

In addition to creating LINQ to SQL classes, we also need a class (TaskData) that will carry data between the client and the server. The TaskData class acts as a data contract and is shown in Figure 9. Note: The TaskData class can be found in the Service.cs file in the code download.

[DataContract]
public class TaskData
{
  public TaskData()
  {
  }

 public TaskData(Guid id,string title,string  
 description,int priority,int status,DateTime duedate)
 {
  this.TaskID = id;
  this.Title = title;
  this.Description = description;
  this.Priority = priority;
  this.Status = status;
  this.DueDate = duedate;
 }

 [DataMember]
 public Guid TaskID { get; set; }
 [DataMember]
 public string Title { get; set; }
 [DataMember]
 public string Description { get; set; }
 [DataMember]
 public int Priority { get; set; }
 [DataMember]
 public DateTime DueDate { get; set; }
 [DataMember]
 public int Status { get; set; }
}

The TaskData class is marked with the DataContract attribute, indicating that it is a WCF data contract. The TaskData class defines six properties that correspond to the Tasks table columns. All the properties are marked with the DataMember attribute, indicating that they will be serialized between the client and the server.

We are now ready to implement the methods of the IService interface. Let's discuss them one by one.

The Insert() method, shown in Figure 10, adds a new task to the Tasks table. The method accepts task details as an object of the TaskData type and returns an integer indicating the success or failure of the operation (though we will not implement the error handling part here). The complete code of the Insert() method is shown in Figure 10.

public int Insert(TaskData t)
{
 Task newTask = new Task();
 newTask.TaskID = Guid.NewGuid();
 newTask.Title = t.Title;
 newTask.Description = t.Description;
 newTask.Priority = t.Priority;
 newTask.Status = t.Status;
 newTask.DueDate = t.DueDate;
 dataContext.Tasks.InsertOnSubmit(newTask);
 dataContext.SubmitChanges();
 return 0;
}

The Insert() method creates an instance of LINQ to SQL class named Task and sets its properties. It then inserts the new object using the InsertOnSubmit() method. The changes are committed to the database when the SubmitChanges() method is called.

The Update() method, shown in Figure 11, accepts a task to be modified and saves the modified task into the database using LINQ to SQL. The Update() method first filters the task to be updated based on its TaskID. It then assigns the modified values of the task. Finally, the SubmitChanges() method saves the modified task to the database.

public int Update(TaskData t)
{
 IQueryable existingTask = from tasks in  
   dataContext.Tasks 
   where tasks.TaskID==t.TaskID
   select tasks;
 Task obj = existingTask.SingleOrDefault();
 if (obj != null)
 {
   obj.Title = t.Title;
   obj.Description = t.Description;
   obj.Priority = t.Priority;
   obj.Status = t.Status;
   obj.DueDate = t.DueDate;
   dataContext.SubmitChanges();
 }  
 dataContext.SubmitChanges();
 return 0;
}

The Delete() method, shownin Figure 12, accepts the TaskID of the task to be deleted and deletes the corresponding task record.

public int Delete(Guid id)
{ 
 IQueryable existingTask = from tasks in   
      dataContext.Tasks where 
     tasks.TaskID == id
     select tasks;
 Task obj = existingTask.SingleOrDefault();
 if (obj != null)
 {
   dataContext.Tasks.DeleteOnSubmit(obj);
   dataContext.SubmitChanges();
 }
 return 0; }

The SelectAll method, shownin Figure 13, returns all the available tasks as an array of TaskData objects.

public TaskData[] SelectAll()   
{
 var allTasks = from tasks in dataContext.Tasks
   orderby tasks.DueDate
   select tasks;
 List items = new List();
 foreach (var obj in allTasks)
 {
  items.Add(new  TaskData(obj.TaskID,obj.Title,
  obj.Description,(int)obj.Priority,
  (int)obj.Status,(DateTime)obj.DueDate));
 }
 return items.ToArray();
}

Notice that since Task and TaskData are different classes (one is a LINQ to SQL class whereas the other is a data contract), we need to transfer all the data from Task objects into TaskData objects, then call the ToArray() method of the generic List class to get an array of TaskData objects.

Configuring the WCF Service and Creating a Web Form

Before putting the WCF service in use, we have to make sure that it’s configured correctly. The section of the web.config file, shown in Figure 14, contains all the configuration needed by our WCF service. The lines at callout A are especially important to us. The name attribute of the tag specifies the fully qualified service class name (Service, in our case). A WCF endpoint allows the client to access the functionality exposed by a service. The contract attribute specifies the fully qualified name of the service contract interface (IService).


 
  
   
     
     
   
  
  
   
    
   
 


BEGIN CALLOUT A
 
  
 
END CALLOUT A
 

Now that we have completed the WCF service, let's move on to the client-side development using jQuery. The task-list application consists of a single web form logically divided into two parts. The top part of the web form, shown in Figure 15, allows a task to be added or edited.

Figure 15: Area for adding or editing a task
Figure 15: Area for adding or editing a task

The task entry region consists of common ASP.NET server controls, such as TextBox, DropDownList, and Button. Although we will not be using any server-side features of these controls in our code, you can make use of such features if, for example, you need server-side validations. Notice the Due Date entry field. The date picker displayed therein is actually a jQuery plug-in we will build later on.

The bottom part of the web form consists of a table that lists all the previously entered task records in order of oldest to newest due date. The task list table also has check boxes to select one or more tasks for deletion. Just above the task list table there is a drop-down list that allows us to filter tasks on the basis of priority or status. Figure 16 shows the task list table with few sample tasks.

Figure 16: Task list table with sample tasks
Figure 16: Task list table with sample tasks

jQuery Template Behind the Task List Table

We will be rendering the task list table as shown in Figure 16 using the jQuery Templates plug-in. So we need to ensure that our web form has the following

After these sections are added, we can use jQuery templates plug-in markup. Figure 17 shows this markup.

Have a look at the

  Title Description Priority Status Due Date  

The first column renders check boxes so that users can mark one or more tasks for deletion. Columns 2 through 5 display property values of the TaskData object. Recall that the SelectAll() method of our WCF service returns an array of TaskData objects. This array is to be displayed in the table row as defined by the jQuery template. To access individual properties of a TaskData object, we use the following syntax:

${}

Thus, ${Title} will return the value of the Title property for a specific TaskData instance.

jQuery templates allow you to render HTML conditionally. For example, we are storing the priority and status information of a task as integer values in the database, but while displaying them, we would like them to be string literals (e.g., "High", "Low"). The if-else construct of jQuery templates allows us to test for some conditions and output HTML accordingly.

The DueDate value returned by the WCF service is first converted into JavaScript-understandable form using the ToJSDate() function. We will discuss this more in later sections.

The last column of the template contains an HTML button. Clicking the Show button will display that task record for editing. Notice how we have assigned the TaskID value to the ID attribute of the button. We did this because we need to know which task is being selected for display.

Although our template is ready, it will not display anything on its own unless we render its contents somewhere in the web form. In our example, we will use a table (taskTable) for that purpose, as shown in the latter part of Figure 17.

Creating the DatePicker Plug-in

We will develop a simple yet convenient date picker plug-in to select the due dates for our tasks (see Figure 15). In order to create our date picker plug-in, we need to add a separate JavaScript (.js) file and place the plug-in code in it. The skeleton of plug-in code is shown in Figure 18.

(function( $ ){
  $.fn.DatePicker = function(method) {
  //plugin code goes here
  };
})( jQuery );

What we are doing here is defining a jQuery plug-in named DatePicker. The DatePicker plug-in is going to have a couple of methods. We will pass the method name as a parameter and invoke certain code accordingly. The skeleton we use ensures that our plug-in won't collide with other libraries that might also use the dollar sign.

Note: The official jQuery website (jquery.com) has documented a set of recommended practices for writing custom plug-ins. If you are not familiar with writing jQuery plug-ins, you should read that documentation as well.

The complete definition of the DatePicker plug-in is shownin Figure 19.

$.fn.DatePicker = function (method) {
 if (methods[method]) {
  return methods[method].apply(this,  
   Array.prototype.slice.call(arguments, 1));
 } else if (typeof method === 'object' || !method) {
  return methods.init.apply(this, arguments);
 } else {
  $.error('Method ' + method + ' does not exist on 
  jQuery.DatePicker');
 }
};

The code in Figure 19 checks the method name that is passed and calls that method from the methods object literal. The methods object literal basically holds the individual methods as a series of name-value pairs so that they can be conveniently called. We need three methods for the DatePicker plug-in: init(), setDate(), and getDate(). If the method parameter is missing, we call the init() method. A skeleton object literal declaration will look like this:

var methods = {
   init: function (options) {…},
   setDate: function (value) {…},
   getDate: function () {…}
};

Let's look at each of the methods. The init() method renders the DatePicker user interface, consisting of three "; var monthHTML = ""; var monthNames = new Array("January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"); for (var i = 0; i < 12; i++) { monthHTML += ""; } monthHTML += ""; var defaults = { prev: 3, next: 3 }; var options = $.extend(defaults, options); var dt = new Date(); yearHTML = ""; $(this).append(dateHTML); $(this).append(monthHTML); $(this).append(yearHTML); }); },

The init() method programmatically creates three elements. The elements will be added to the DIV element that we specify.

Notice one useful configuration feature, the $.extend() function. We may need to control the days, months, and years being filled in the DatePicker. For example, we may want to fill the year drop-down list with the next three years only. Such a configuration can be accomplished by using the $.extend() function. First we define some defaults. For example, { prev: 3, next: 3 } will display the three previous years and three future years. If no configuration is supplied via the init() method parameter, this default configuration will be used. If, however, some different configuration is provided, we will use the $.extend() method to merged this different configuration with the default configuration. This way, we may configure just the previous number of years or just the next number of years displayed, keeping the other value to its default.

Notice that the init() method returns this to maintain the chainability of jQuery code.

The setDate() method sets the current selection of the three

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish