Task Status Manager With ECMAscript and jQuery UI

February 1, 2011

This post is over a year old, some of this information may be out of date.

What if you could manage your task statuses by just a drag and drop action? Wouldn’t that be cool? This was the idea that a colleague and I had last week.

Concept

For this concept I am only going to use the following task statuses:

  • Not Started
  • In Progress
  • Completed To visualise the task items, I have created three task status blocks.
Concept layout
Concept layout

The task items will be retrieved with the SharePoint 2010 ECMAscript client object model. jQuery and jQuery UI will be used to place the task items on the page and be able to drag and drop them.

How it is worked out

The task list that I am going to use contains the following items:

Task list items
Task list items

On a new page I added my conceptual layout to the page and the jQuery and jQuery UI script references inside a “Content Editor” web part. To be a more flexible, I also added another script reference that will contain the code for this solution.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<script src="jquery.min.js" type="text/javascript"><!--mce:0--></script>
<script src="jquery-ui-1.8.9.custom.min.js" type="text/javascript"></script>

<script src="EcmaTest.js" type="text/javascript"></script>
<link href="/_layouts/styles/tasks/EcmaTest.css" rel="stylesheet" type="text/css"/>

<div id="taskbox">
  
  ### Tasks
  
  <div id="task-blocks">
    <div class="block col">
      <div class="task-status notstarted">
        **Not Started**
      </div>
      <div id="notstarted" class="task-items">
      </div>
    </div>
    
    <div class="block col">
      <div class="task-status inprogress">
        **In Progress**
      </div>
      <div id="inprogress" class="task-items">
      </div>
    </div>
    
    <div class="block col">
      <div class="task-status completed">
        **Completed**
      </div>
      <div id="completed" class="task-items">
      </div>
    </div>
    
    <div class="clear"></div>
  </div>
</div>

Retrieve the task list items

The first thing that needs to be done is retrieving all the task items from the task list.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
var clientContext = null;
var web = null;
ExecuteOrDelayUntilScriptLoaded(Initialize, "sp.js");

//Retrieve all the task items
function Initialize() {
  //Get the current SP context
  clientContext = new SP.ClientContext.get_current();
  web = clientContext.get_web();
  //Set the correct list
  var list = web.get_lists().getByTitle("Tasks");
  var camlQuery = new SP.CamlQuery();
  var q = "<Where></Where>";
  camlQuery.set_viewXml(q);
  this.listItems = list.getItems(camlQuery);
  //Only retrieve the "ID", "Title" and "Status" fields.
  clientContext.load(listItems, 'Include(ID, Title, Status)');
  //Execute the listitem query
  clientContext.executeQueryAsync(Function.createDelegate(this, this.onListItemsLoadSuccess), Function.createDelegate(this, this.onQueryFailed));
}

//If the ecmascript failed, output the error in an alert.
function onQueryFailed(sender, args) {
  alert('request failed ' + args.get_message() + '\n' + args.get_stackTrace());
}

function onListItemsLoadSuccess(sender, args) { }

The “onListItemsLoadSuccess” function will be used to place the task items to the correct task status blocks.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
//On success, put the task items to the correct list
function onListItemsLoadSuccess(sender, args) {
  var listEnumerator = this.listItems.getEnumerator();
  //Iterate though all of the items
  while (listEnumerator.moveNext()) {
    //Retrieve the current list item
    var oListItem = listEnumerator.get_current();
    var status = oListItem.get_item('Status');
    
    //Add the items to the correct list
    if (status == "Not Started") {
      $("#notstarted").append("<div class='item' ref='" + oListItem.get_item('ID') + "'>" + oListItem.get_item('Title') + "</div>");
    }
    else if (status == "In Progress") {
      $("#inprogress").append("<div class='item' ref='" + oListItem.get_item('ID') + "'>" + oListItem.get_item('Title') + "</div>");
    }
    else if (status == "Completed") {
      $("#completed").append("<div class='item' ref='" + oListItem.get_item('ID') + "'>" + oListItem.get_item('Title') + "</div>");
    }
  }
}

This piece of code will result in:

Recieved tasks
Recieved tasks

Each task item contains its “ID” in a “ref” attribute.

1
<div ref="1" class="item">Task 1</div>

Enable drag and drop

The task items are added with JavaScript to the page, this means that you cannot directly add the “draggable” handler to the task items. A solution for this problem is to live bind the “draggable” handler to the task items. The following jQuery helper function can be used to solve this problem.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
//This are the jQUery Live plugins.
//This plugin enable you to bind the draggable event to the items that are placed by an Ajax call.
(function ($) {
  jQuery.fn.liveDraggable = function (opts) {
    this.live("mouseover", function () {
      if (!$(this).data("init")) {
        $(this).data("init", true).draggable(opts);
      }
    });
  };
})(jQuery);

When this function is added to the JavaScript file, the jQuery code can be written.

  1. Attach the “draggable” handler to the task items;
  2. Make it possible to drop items on the status blocks;
    • Add a new item to the status block when dropped;
    • Remove the dropped item, otherwise two task items will exists;
    • Create a call to the update function.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
$(function () {
  //Attach the draggable handler to the task items
  $(".item").liveDraggable({ containment: "#task-blocks", scroll: false });
  
  //Make it possible to drop items on the status boxes
  $("#notstarted, #inprogress, #completed").droppable({
    drop: function (event, ui) {
      $("<div></div>").attr('class', 'item').attr('ref', ui.draggable.attr('ref')).text(ui.draggable.text()).appendTo(this);
      deleteItem(ui.draggable);
      changeItemToStatus(ui.draggable.attr('ref'), $(this).attr("id"));
    }
  });
});

//Delete the dropped item when it is placed on a new list.
function deleteItem($item) {
  $item.fadeOut();
}

//Change the moved task item
function changeItemToStatus(itemID, newStatus) {}

Currently it is only possible to drag around the items and drop them on another status block. The next step is to write update function.

Update the task item status

The update function will make use of the SharePoint ECMAscript client object model.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
//Change the moved task item
function changeItemToStatus(itemID, newStatus) {
  clientContext = new SP.ClientContext.get_current();
  web = clientContext.get_web();
  var list = web.get_lists().getByTitle("Tasks");
  
  this.listItem = list.getItemById(itemID);
  
  //Set the new status
  if (newStatus == "notstarted") {
    listItem.set_item('Status', 'Not Started');
  }
  else if (newStatus == "inprogress") {
    listItem.set_item('Status', 'In Progress');
  }
  else if (newStatus == "completed") {
    listItem.set_item('Status', 'Completed');
  }
  
  listItem.update();
  clientContext.load(listItem);
  //Execute the query
  clientContext.executeQueryAsync(Function.createDelegate(this, this.onQuerySucceeded), Function.createDelegate(this, this.onQueryFailed));
}

//Show an alert if the item is successfully updated.
function onQuerySucceeded() {
  alert('Item updated!');
}

The “onQueryFailed” function is reused from the “Retrieve the task list items” section.

Download

Download the source files.

Comments

comments powered by Disqus