Last week one of my customers at hybrIT Services wanted me to implement drag and drop to update a status field for one of their lists. So I found the Andrew Burgess’ article on how to implement drag and drop.
Creating the page
The list that we looked at contained sales opportunities that moved through stages such a New Opportunity, Win, Unsuccessful and Work In Progress.
To create the list and tabs I’ve created a page with two web parts. The tabs are specified in a Content Editor Webpart and a list view web part is positioned straight underneath these tabs.
The html for the tabs is quite simple:
[code lang=text]
<div class=”post-tabs”>
<ul class=”tabs-nav”>
<li class=”current”>< a href=”./Recent Wins.aspx”> Recent Wins< /a> < /li>
<li class=””>< a href=”./Decision Pending.aspx”> Decisions Pending< /a> < /li>
<li class=””>< a href=”./Action Required.aspx”> Work in Progress< /a> < /li>
<li class=””>< a href=”./NewOpportunities.aspx”> New Opportunities< /a> < /li>
<li class=””>< a href=”./Unsuccessful No Bid.aspx”> Unsuccessful< /a> < /li>
<li class=””>< a href=”./No Bid.aspx”> No Bid< /a> < /li>
</ul>
< /div>
[/code]
For this post I’m going to ignore the CSS needed to make the tabs look like tabs as I’m focusing on the drag and drop functionality.
To implement drag and drop I’m going to add some JavaScript to the Content Editor Webpart.
Drop JavaScript
Looking at the above html we can now use the following JavaScript to select the tabs and make them available for dropping items into.
[code lang=text]
$(‘ul.tabs-nav li’)
[/code]
Drag JavaScript
Then within the DOM Explorer in IE I found that my listitems have a class of ms-draggable available. Nice and easy.
So the following JavaScript will get me the items in my list.
[code lang=text]
$(‘.ms-draggable’)
[/code]
Binding the Events
I’m now creating a JavaScript function AttachDragDrop to make the drag and drop work.
To make the title of the item draggable, I don’t really need to do anything as Microsoft have already made the titles draggable by default. However I would like to collect the id of the list item and therefore I’m binding the dragstart event with the following lines.
[code lang=text]
$(‘.ms-draggable’)
.bind(‘dragstart’, function (evt) {
evt.dataTransfer = evt.originalEvent.dataTransfer;
evt.dataTransfer.setData(‘text’, this.parentElement.id);
});
[/code]
Note that this.parentElement.id gets me to the id of the div around the link. So in the above example in the DOM Explorer this is giving me 4159. Thanks you Microsoft for making this easy!
So now every time I’m dragging a title of an item I’m getting some data set with the list items id, which I can then use during the drop event.
Now I’m ready to implement the drop:
[code lang=text]
$(‘ul.tabs-nav li’)
.bind(‘dragover’, function (evt) {
evt.preventDefault();
})
.bind(‘dragenter’, function (evt) {
evt.preventDefault();
})
.bind(‘drop’, function (evt) {
// Here I’m going to implement the drop code.
}
[/code]
Updating the list items
First I want to get the list item id that we stored during the dragstart event.
[code lang=text]
var id = evt.originalEvent.dataTransfer.getData(‘text’)
[/code]
As I’ve got multiple tabs that I may drag my items into I need to identify which one I’ve dragged the item to.
[code lang=text]
var newStatus = this.textContent;
[/code]
So now my newStatus will contain the statuses that I’ve implemented in my tabs.
So all I’ve now got left to do is update the item in SharePoint and then hide the item from my list once the item has been updated successfully.
So first I’m building my client context.
[code lang=text]
var siteUrl = “https:/ /mytenant.sharepoint.com/mylist”;
var clientContext = new SP.ClientContext(siteUrl);
[/code]
then I’ll get my list:
[code lang=text]
var oList = clientContext.get_web().get_lists().getByTitle(‘Pipeline’);
[/code]
Then I get the item that I need to update
[code lang=text]
this.oListItem = oList.getItemById(id);
[/code]
Wow, this is easy!
Now I need to internal name of my choice field that I need to update. So I’m going to my list settings and I’m finding the internal field name in the Url:
Ahhh, people have been using spaces when creating the field. Always remember to create fields without spaces first and then rename the fields. In this case I’m using the spaced out field name and replace the %5F with and underscore ( _ ) resulting in Pipeline_x0020_Stage.
Then I checked out the choice values and unfortunately they don’t match my tabs.
No worries, I simple create a bit of JavaScript that matches my tabs with values in the list column and then update the list item followed by an Async call.
[code lang=text]
switch(newStatus) {
case “No Bid”: this.oListItem.set_item(‘Pipeline_x0020_Stage’, ‘J – No Bid’);
break;
case “Unsuccessful”: this.oListItem.set_item(‘Pipeline_x0020_Stage’, ‘K – Unsuccessful’);
break;
case “Decisions Pending”: this.oListItem.set_item(‘Pipeline_x0020_Stage’, ‘F – Solution evaluation’);
break;
case “Work in Progress”: this.oListItem.set_item(‘Pipeline_x0020_Stage’, ‘E – Propose Solution’);
break;
case “New Opportunity”: this.oListItem.set_item(‘Pipeline_x0020_Stage’, ‘A – New Opportunity’);
break;
default: break;
}
this.oListItem.update();
$(“#” + id).parent().parent().hide();
clientContext.executeQueryAsync(onQuerySucceeded, onQueryFailed);
});
[/code]
Then for completeness of this article I’m including an onQuerySucceeded and onQueryFaileded, where I’m returning some information to the console of the browser.
[code lang=text]
function onQuerySucceeded() {
console.log(‘Item updated!’);
}
function onQueryFailed(sender, args) {
console.log(‘Request failed. ‘ + args.get_message() + ‘\n’ + args.get_stackTrace());
}
[/code]