Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

ToDo List using ASP.NET MVC

1 Jun 2009 1  
Creating a task list using MVC

Introduction

It has been quite a while since MVC has been released. In this article, I will sample out an application for managing tasks (to-do lists) using ASP.NET MVC. The first alpha version is compiled against native features of MVC so as to give a glimpse of the native features. Future releases will have jQuery plug-in and complete DAL based ToDo List. You can watch a live demo and download the code here.

The list (Index action) of TodoItem has been updated using the jquery dataTable plug-in. Thanks to Matt Berseth, for iTunes theme for GridView which has been modified and adapted for the dataTable. The screenshot for the same is shown below: 

index.png

Background

Before we can start off with coding, get hold of MVC pattern and download the Visual Studio 2008 extras from ASP.NET. You can get a kick-start from various videos and tutorials available from ASP.NET website.

The previous versions just used native MVC, i.e., no Data layer was added to the project. Now, if you look at the source, you will see, the code is more organized (quite a few comments though!). But, the Controller doesn't even know what it is doing, just that it knows of entity (TaskItem). What's more in this update to this article is the Using Code section is almost all over re-written according to this release.

Before proceeding further, let's have a look at what the solution has to offer! Below is a quick look at the Data project.

Data project

Using the Code

The entity has been renamed to TaskItem from ToDoItem, though the controller name has been kept the same so as to keep the URLs the same. Let's have a look at the code from our entity class in Model.

  public partial class TaskItem
  {
    public int ItemId { get; set; }
    public string Title { get; set; }
    public int Priority { get; set; }
    public DateTime StartDate { get; set; }
    public DateTime? DueDate { get; set; }
    public int? PercentCompleted { get; set; }
    public bool? TrackProgress { get; set; }
    public bool? RemindOnDelay { get; set; }

    #region object overrides
    public override bool Equals(object obj)
    {
      if (obj is TaskItem)
      {
        TaskItem item = (TaskItem)obj;
        return item.ItemId == this.ItemId;
      }

      return base.Equals(obj);
    }

    public override string ToString()
    {
      return this.Title;
    }

    public override int GetHashCode()
    {
      return this.ItemId.GetHashCode();
    }
    #endregion
  }

The credit for this IQuerable pattern implementation goes to Rob Conery. If you want a more detailed tutorial on IQueryable and the pattern used for todolist, Rob has got video tutorials published on his blog for MVC Storefront application. You can watch them here.

To this partial class, we add validation logic so that there is no more validation on the Controller side, rather everything is accomplished at the Data layer only. The following code checks for any violations (validation errors) in the code.

  public partial class TaskItem
  {
    public bool IsValid
    {
      get { return (GetRuleViolations().Count() == 0); }
    }

    public IEnumerable<RuleViolation> <code>GetRuleViolations()</code>
    {
      if (string.IsNullOrEmpty(Title))
        yield return new RuleViolation("Task title is required.", "Title");

      if (Priority <= 0 || Priority > 3)
        yield return new RuleViolation("Priority must lie between 1 to 3.", "Priority");

      if (DueDate != null && DueDate < StartDate)
        yield return new RuleViolation
		("Due date must be greater than start date.", "Due Date");

      if (PercentCompleted != null && (PercentCompleted <= 0 || PercentCompleted > 100))
        yield return new RuleViolation
		("Please specify Percentage completed between 1 to 100.",
            "Percentage Completed");

      yield break;
    }
  }

The function GetRuleViolations will help us in adding validation checks for the entity operations in the data layer. Thanks to Scott Guthrie et. al. for authoring Professional ASP.NET MVC (a great book!).

We can check for validation errors in operations very easily as shown in the InsertTaskItem function below:

    /// <summary>
    /// Insert the param to database.
    /// </summary>
    /// <param name="item">TaskItem object to be inserted.</param>
    public void InsertTaskItem(TaskItem item)
    {
      if (!item.IsValid)
        throw new ArgumentException();

      using (LinqTaskItemDataContext db = new LinqTaskItemDataContext())
      {
        SqlRepository.TaskItem dbItem = db.TaskItems
          .Where(x => x.ItemID == item.ItemId)
          .SingleOrDefault();
        bool isNew = false;

        if (dbItem == null)
        {
          dbItem = new DevOrigin.ToDoList.Data.SqlRepository.TaskItem();
          isNew = true;
        }

        int itemId = (from taskItem in db.TaskItems
                      select taskItem.ItemID).Max();

        //add the item
        dbItem.ItemID = ++itemId;
        dbItem.Title = item.Title;
        dbItem.Priority = item.Priority;
        dbItem.StartDate = item.StartDate;
        dbItem.DueDate = item.DueDate;
        dbItem.PercentCompleted = item.PercentCompleted;
        dbItem.TrackProgress = item.TrackProgress;
        dbItem.RemindOnDelay = item.RemindOnDelay;

        if (isNew)
          db.TaskItems.InsertOnSubmit(dbItem);

        db.SubmitChanges();
      }
    }

Now, the Service layer project, just implements ITaskItemService interface. The Service class uses the ITaskItemRepository methods to provide services to its user.

As you can see in Controller methods, the code has just reduced to a matter of one or two lines. All the implementation is hidden in the Data layer and thus can easily be changed without affecting the layers working over it until the output remains the same.

Oh, did I forget to mention, how to get that pretty look for data in Index controller. That's easy, huh! I used dataTable jQuery plug-in to get that thing and then a bit of styling to make it look pretty. Let's look at the code in Index view for dataTable.

  <script type="text/javascript" charset="utf-8">
    var oTable;
    var giRedraw = false;
    
    $(document).ready(function() {
      /* Add a click handler to the rows - this could be used as a callback */
      $("#taskList tbody").click(function(event) {
        $(oTable.fnSettings().aoData).each(function() {
          $(this.nTr).removeClass('row_selected');
        });
        $(event.target.parentNode).addClass('row_selected');
      });

      oTable = $('#taskList').dataTable({
        "bAutoWidth": false,
        "bFilter": false
      });
    });

    /* Get the rows which are currently selected */
    function fnGetSelected(oTableLocal) {
      var aReturn = new Array();
      var aTrs = oTableLocal.fnGetNodes();

      for (var i = 0; i < aTrs.length; i++) {
        if ($(aTrs[i]).hasClass('row_selected')) {
          aReturn.push(aTrs[i]);
        }
      }
      return aReturn;
    }
  </script>

The highlighted code will actually render the dataTable in the View. Also, you will notice that a handler has been registered which is used to select the row when the user clicks on any row inside the table. And there is a unused function which is for future use, fnGetSelected which returns the selected row. You can get a full reference of dataTable here.

When you try to create a new task, you get a list of items that you need to enter which are: Title, Priority, Start Date, Due Date, % Completed, Track Progress, and Show Reminder.

Interestingly, "Show Reminder" or ReminderOnDelay, will send out an e-mail to the owner of the task automatically if it lags behind by a given set of configuration criteria though it is not implemented, but it can easily be done. And ToDoList will show you how to do that.

The next plan is to add enhancements and remaining functionality to the todolist.

Points of Interest

Once you download the code from the website, you will find schema.sql file with the help of which you will be able to configure the database. After the database is configured, update the web.config file so that the connection string is appropriately reflected.

History

  • 0.1.3 (alpha)
    • Updated with Repository and Queryable pattern
    • Available on MSDN, Scott Gu's blog, Rob Corney's blog
  • v0.1.2 (alpha)
    • Updated jQuery plugs for Detail action
  • v0.1 (alpha)
    • Initial release

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here