88 articles and 201 comments as of Sunday, October 3rd, 2010

Thursday, May 20, 2010

Client Side AJAX Applications in SharePoint 2010 – Part 4: jQuery Integration and Persistence

Guest Author: Lee Richardson
http://rapidapplicationdevelopment.blogspot.com/

In parts one and two of this series I described how WCF Data Services in SharePoint 2010 exposes list data as both XML and JSON.  In part three, I showed how to set up ASP.Net AJAX Templating and how to write a simple application that displays a list of user story list items.

In this part of the series I’ll show how to integrate with jQuery to display the user stories as draggable index cards on a virtual cork board and how to persist the X and Y coordinates back to SharePoint.

Cards on a Corkboard

The first step in making our application a little more interesting is to set up some CSS styling in the PageHead (or better yet a separate .css file of course) to display the user stories as index cards.

<STYLE type="text/css">
  .sys-template
  {
    display: none;
  }
  .userStoryBackground
  {
    background-image: url('Images/corkboard.png');
  }
  .userStoryCard
  {
    border: 1px solid #777777;
    width: 200px;
    cursor: move;
    background-image: url('Images/blankcard.png');
  }
  .userStoryDescription
  {
    font-size: 13px;
    padding: 0px 5px 5px 5px;
  }
  .userStoryTitle
  {
    font-size: 15px;
    font-weight: bold;
    padding: 0px 0px 0px 0px;
    padding: 0px 5px 0px 5px;
  }
</STYLE>

And update the Main section use the styles:

<asp:content id="Main" contentplaceholderid="PlaceHolderMain" runat="server">
    <div id="userStories" class="sys-template userStoryBackground">
        <div class="userStoryCard">
            <div class="userStoryTitle">{{ Title }}</div>
            <div class="userStoryDescription"><div>{{ Description }}</div>
        </div>
    </div>
</asp:content>

The result looks like this:


Looks good, but we need to absolute position the cards to make them draggable.  First we add X and Y number fields to the User Stories list in SharePoint.  Next we set position to absolute in the CSS style.  Finally, we set the X and Y with the following:

<div id="userStories"
	class="sys-template userStoryBackground"
	xmlns:sys="javascript:Sys">

	<div class="userStoryCard"
		sys:style="{{ 'left: ' + X + 'px; top: ' + Y + 'px;'}}">

		<div class="userStoryTitle">{{ Title }}</div>
		<div class="userStoryDescription"><div>{{ Description }}</div>
	</div>
</div>

There are a couple of interesting things going on here.  First is the sys namespace declaration at the top and its use on the style attribute.  The reason you can’t just use a regular style attribute is that the DataView can’t natively replace certain HTML attributes (if you’re interested in why see this article by Bertrand La Roy).  The sys namespace allows the DataView to get around this HTML limitation.

The second interesting thing is the string concatenation inside of the style attribute’s binding expression.  You can put any JavaScript you like inside of the binding expression, which turns out to be a very powerful feature. 

Now, if we manually set X and Y values in SharePoint for a user story, its card shows in the right place. But we’re still missing the ability to drag cards.

Making Cards Draggable with jQuery

jQuery makes dragging ridiculously simple.  The first step is to download and reference the draggable jQuery UI pluggin (in my case jquery-ui-1.8.1.custom.min.js).  Next we’re supposed to call the draggable() function on the DOM elements we want to make draggable. 

This should be simple enough, but if we call draggable() from within the Sys.onReady function it won’t work.  The reason is that the ASP.Net AJAX Templating engine hasn’t had a chance to render the new DOM elements yet.  Fortunately the DataView has the JavaScript equivalent of an OnRendered event that you can tie into:

var dataContext;
var dataView;

Sys.onReady(function () {
    dataContext = Sys.create.openDataContext({
        '/PreDemo/_vti_bin/ListData.svc'
        );

    dataView = $("#userStories").dataView({
        dataProvider: dataContext,
        fetchOperation: "UserStories",
        fetchParameters: { orderby: 'Title' },
        autoFetch: "true",
        rendered: onRendered
    }).get(0);
});

function onRendered() {
    $(".userStoryCard").draggable();
}

Incidentally, you may have noticed that this code has been updated slightly from the code in part three.  The biggest change is that we are now using an openDataContext instead of an openDataServiceProxy.  The reason is that the openDataContext knows how to write data back to SharePoint (which we will be doing shortly).

But more importantly we can now drag user stories all over the page:


It’s pretty amazing what jQuery can do.  But we still have a problem.

Saving Non-Visible Properties

The problem is that every time we refresh the page the nice card layout disappears and everything goes back to being stacked one on top of the other in the upper left.  

One approach to saving the X and Y coordinates would be to use the live binding syntax that I’ll discuss in more detail in the next post.  However, for non-visible properties the more appropriate way is to manually edit the templating engine’s underlying, in-memory, JSON objects.  This will additionally give us a little more insight into what’s happening behind the scenes.

Step one is to register an “event” for when the user stops dragging a card.

function onRendered() {
    // from http://jqueryui.com/demos/draggable/
    $(".userstorycard").draggable({
        stop: onDragStop
    });
}

Once we wire that up the onDragStop() function is where all the interesting stuff happens:

function onDragStop(event, ui) {
    var userStoryCard = ui.helper[0];
    var selectedUserStoryJsonObject =
        dataView.findContext(userStoryCard).dataItem;

    var newX = ui.position.left;
    var newY = ui.position.top;

    Sys.Observer.setValue(selectedUserStoryJsonObject, "X", newX);
    Sys.Observer.setValue(selectedUserStoryJsonObject, "Y", newY);
    dataContext.saveChanges();
}

The first line gets the DOM element that the user just completed dragging (from jQuery UI). The second line retrieves the in-memory JSON object represented by that DOM element. The next couple of lines get the X and Y coordinates that the user story card was dragged to (relative to their parent DOM element).  Then something odd happens.

Sys.Observer.setValue looks so complicated. Why couldn’t we just call selectedUserStoryJsonObject.X = newX?  If we’d done that, the DataView wouldn’t know about the change we just made and dataContext.saveChanges() wouldn’t know to send anything back to the server.  

Sys.Observer.setValue notifies any interested parties that a value has been changed, in this case the DataView.  This notification technique enables a wonderful feature of ASP.Net AJAX Templating wherein the dataView batches up changes.  Specifically, if we watch what happens over the wire with Fiddler when we call dataContext.saveChanges() we’ll see that it only sends records that have changed back to SharePoint.  This feature can save a lot of network traffic and speed up the responsiveness of applications.

Now with this code in place, we can move user story cards around on the page and when we refresh the page or come back days later our layout has persisted back into SharePoint!

Conclusion

In this post I’ve shown how nicely ASP.Net AJAX Templating interacts with jQuery and how to directly modify in-memory JSON objects and send them back to SharePoint.  The application is beginning to look like a useful application.  And it’s remarkable how little code it’s taken.   In the next post in the series I’ll show how to do master-detail scenarios for editing user story cards.

Guest Author: Lee Richardson
http://rapidapplicationdevelopment.blogspot.com/

Lee Richardson is a Senior Software Engineer at Near Infinity Corporation, an enterprise software development and consulting services company based in Reston, Virginia. He is a Microsoft Certified Solution Developer (MCSD), a Project Management Professional (PMP), a Certified SCRUM master and has over ten years of experience consulting for the public and private sector. You can follow Lee on Twitter at @lprichar

Bookmark and Share
 

Please Join the Discussion

One Response to “Client Side AJAX Applications in SharePoint 2010 – Part 4: jQuery Integration and Persistence”
  1. Bruno says:

    Hi,

    I’m having some troubles with SP2010 and abolsute position.

    Unlike SP2007, in 2010 when I set position:absolute, it is still relative to the webpart holder.

    In 2007 I used absolute position to simulate popups but in 2010 that seems not possible.

Subscribe without commenting

Speak and you will be heard.

We check comments hourly.
If you want a pic to show with your comment, go get a gravatar!