Click here to Skip to main content
Click here to Skip to main content

ASP.NET MVC Application in Action - I (DailyJournal)

, 7 Jun 2010 CPOL
Rate this:
Please Sign up or sign in to vote.
A simple visual task manager application using ASP.NET MVC and jQuery.

Table of Contents

Introduction

It has been long due I was planning to write an article on creating some useful ASP.NET MVC application. I have codenamed it "DailyJournal". It is a simple application which allows creation of multiple activities and assigning tasks to these activities. It's kind of a "Yet another Task/Todo application".

The application makes use of jQuery, so some knowledge is assumed of it. The application also makes of the the jQuery UI plug-in.

For the basics of jQuery, you can refer any of the numerous resources, or my article here at CodeProject: Getting Friendly With jQuery.

The credentials which you can use with the attached demo application are shown below:

User Name : admin 
Password  : admin123

Framework/Libraries Used

  • ASP.NET MVC
  • jQuery + jQuery UI (for AJAX and UI)
  • ELMAH for error logging

Warning Ahead

This is just a rough draft, and so I am putting down some of the known limitations.

Some points of warning before we move further with this application. This is just an early prototype. As such, many of the design principles have been ignored. But, I will try to cover that up in the next update once I get my head around this.

The application, in its current state, supports the following features:

  • Create users
  • Assign activities to users
  • Assign tasks to activities
  • Assign a status to a task

The user creation/authentication is being done by the default Membership Provider. Most of the activities are highly visual, i.e., you can drag-drop tasks to different areas, in-place edition of task details, and so on.

The following are the current issues with the design, which I promise to refactor in the second version:

  • No validations.
  • Fat Controller.
  • XSS/CSS vulnerable.
  • No service model/abstraction yet. For the demo, LINQ to SQL is implemented.
  • No separation of layers.
  • UI Design.
  • et el...

Note: The second iteration will cover the following points:

  • Major refactoring
  • All the above issues
  • Unit tests
  • Add extensible validation
  • IOC
  • Theme support

Screens in Action

Once you log in to the system, you are presented with the following screen which lists the current activities:

You can add a new activity by clicking on the "New Activity" button. It brings up a dialog box.

jQuery UI Dialog

The dialog is created using the "jQuery UI Dialog" plug-in. To create a dialog using the jQuery UI, use the following two steps:

  1. Define a div which should be shown as the dialog and give it an ID (you can also give it a CSS class).
  2. <div id="dlg-work-area">
      <% Html.RenderAction("AddWorkArea", "Work"); %>
    </div>
  3. To invoke the dialog, add the following code in the jQuery ready function:
  4. $(function()  {
        $("#dlg-work-area").dialog( {
            autoOpen: false,  
            title: "Add Activity",
            height: 250,
            width: 350,
            modal:true
        });
    }
  5. Hook the click event handler on the "Add New Activity" button.
  6. $("#btnNewActivity").click(function() 
    {
        $("#dlg-work-area").dialog('open');
            });

Note: We have set autoOpen to false as we want the dialog to open when the "Add New Activity" button is clicked.

Once you select an activity, you are taken to the task screen. Here you can create a new task, and change the task status by dragging and dropping within the available columns. There is a minor issue with drag-drop with this version as the column headers are also draggable. This will be fixed.

In-Place Edit

You can edit the task inline. Double click on the task description to do so. It will bring up the following screen. You can save or cancel the changes.

In place editing is achieved by the code snippet outlined below. Refer to the widget.js file under the /scripts/widget folder.

Set up the events and in-place editing in the onready function of jQuery:

$(function() {
    setEditable();
});

The setEditable() function is defined below:

function setEditable() {
    $('.edit').click(function() {
        var textarea = '<div><textarea>'+$(this).html()+'</textarea>';
        var button     = '<div><input type="button" value="SAVE" ' + 
          'class="smallButton saveButton" /> <input ' + 
          'type="button" value="CANCEL" ' + 
          'class="smallButton cancelButton" /></div></div>';
        var revert = $(this).html();
        $(this).after(textarea+button).remove();
        $('.saveButton').click(function(){saveChanges(this, false);});
        $('.cancelButton').click(function(){saveChanges(this, revert);});
    })
    .mouseover(function() {
        $(this).addClass("editable");
    })
    .mouseout(function() {
        $(this).removeClass("editable");
    });
};

The above code does the following things:

  1. Find all elements with the class "edit".
  2. Build up the text area and button.
  3. Hook up the events for the Save and Cancel buttons.

For details about this, refer to the widget.js file.

Basic Model Diagram

The below is the bare bone model diagram:

Routes

The application in this prototype uses the routes that is defined in the global.asax.cs, which is of the format:

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

The default "Controller" is the "Dashboard" and the default action is "Index". The routes follow the basic convention of controller/action/{optional ID} pattern.

Interesting Code Snippets

The dashboard drag-drop uses the jQuery UI Sortable plug-in. This can be found in the widget.js file.

function reBind(element) {
    $(element).sortable({
        connectWith: '.column',
        receive: function(event, ui) 
        {
            var target =  event.target.id;
            if (null != target)
                target = target.substring(target.lastIndexOf("_")+1);
            
            var taskId = ui.item.attr('id')
            
            if (null != taskId)
                taskId = taskId.substring(taskId.lastIndexOf("_")+1);

            var postdata = "taskStatusId=" + target + "&taskId="+taskId ;
            $.post("/work/savewidget?" + postdata, function(data){
                
            });
        }
    });
}

What it does is find all the elements with a class of "column" and apply sortable behaviour on it. The code to save the widget location is in the "receive" event handler. The data is collected and sent to the "Work" Controller's "SaveWidget" action.

The controller code to render the task widgets is shown below. If there are no task status (as tasks are grouped by status), the user is taken to the "AddTaskStatus" screen.

[HttpGet]
public ActionResult Board(Guid workAreaId)
{
    
    var allStatus = db.TaskStatus.Where(ts => ts.WorkAreaId == workAreaId).ToList();

    if (allStatus.Count == 0)
    {
        return RedirectToAction("AddTaskStatus", new { workAreaId = workAreaId });
    }

    ViewData["AREA_ID"] = workAreaId.ToString();
    ViewData["AllStatus"] = allStatus;

    return View(allStatus);
}

The inline task editing is passed to the following controller action:

[HttpPost]
public void SaveTaskDesc(Guid TaskId, string desc)
{
    var task = db.Tasks.Where(t => t.Id == TaskId).SingleOrDefault();

    task.Description = desc;
    db.SubmitChanges();
}

ELMAH Config

<sectionGroup name="elmah">
  <section name="errorLog" requirePermission="false" 
      type="Elmah.ErrorLogSectionHandler, Elmah" />
</sectionGroup>

<elmah>
<errorLog type="Elmah.XmlFileErrorLog, Elmah" 
   logPath="~/App_Data" />
</elmah>

<httpHandlers>
   <add verb="POST,GET,HEAD" path="elmah.axd" 
      type="Elmah.ErrorLogPageFactory, Elmah" />
</httpHandlers>

First, we define a section for "elmah". Then we set the log to an XML log file. And then the most important thing is to setup the HTTPHandler. By setting up ELMAH, the error logging functionality is taken care of. The URL to access the errors is: http://serverurl/elmah.axd.

I know there are lots of limitations, but I hope that with further iterations, it will get better and better.

The second version of this article is almost ready. I am doing some refactoring and cleanup and putting some final design thoughts.

References

History

  • June 08, 2010 - Updates and corrections.
  • May 20, 2010 - Early prototype.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Rajesh Pillai
Architect
India India
Co Founder at TekAcademy Labs.
 
http://tekacademy.com/
Follow on   Twitter

Comments and Discussions

 
Generalgr8 work PinmemberStart Shining24-Mar-11 20:16 
GeneralRe: gr8 work PinmemberRajesh Pillai16-Apr-11 10:04 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141220.1 | Last Updated 8 Jun 2010
Article Copyright 2010 by Rajesh Pillai
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid