This is by no means an attempt to define how database/DOM updates via AJAX should be handled. It is more a look back at how I did handle them and an opportunity to gather input from others. So please use the comments or drop me an email and let me know your thoughts.

Before we begin, here is a bit of background. This article is based on a project that was a custom .NET app built inside of SharePoint. The front end used jQuery and was interacting with .NET web services which handled the database operations.  All data sent to and received from the web services was structured as JSON.

Now, on to the interesting stuff.

In a web application we want users to be able to make changes to their data easily, quickly, and seamlessly. What this boils down to is inline creation and editing of content without page refreshes. For the purposes of this article we'll focus on creating a comment. Comments appear on many websites in a variety of contexts and they don't require a lot of data. They are a good example but the approach we'll talk about can certainly be used for more complex operations.

When I think about the interaction described above I immediately have two questions in my mind.

  • How do we structure our code so it is reusable, legible, and DRY?
  • How do we make the same type of call from anywhere in the site while maintaining the context in which we want to handle the response?

The first applies to coding in general but the second is more specific to our particular task.

So, how do we structure the code so it is reusable, legible and DRY? First we want create a method for interacting with the web service which will update our database. This method will provide a single function that any script on the site can use to create a comment in the same way. The call would look something like this:


Comments may be used on any page in the site. The scripts and markup may vary from one instance to another but in our application regardless of the location, design, or user experience when we create a comment the call to the web service is the same. It saves a piece of text and associates it with a piece of content.

This brings us to the second question. How do we make the same type of call from anywhere in the site while maintaining the context in which we want to handle the response? Presumably the response will contain something we want to insert into the DOM like the HTML for the new comment or maybe even a refresh of the entire comment list. How do we structure our call so the response data, whenever it may arrive, is returned to the right location and dealt with properly?

What do I mean when I say returned to the right location? Say there are 20 different places on a single page where you can enter a comment. Say half of those are to comment on blog posts and half are to comment on a photo feed. When the AJAX response is returned how do you know which thread list to update or which spinner to remove? My thinking is that you pass the response back to the button which was clicked and use that context to handle the next steps. Presumably the button will be in the same general area as the thread and spinner so you can use simple jQuery selectors to make the necessary DOM updates.

There are two elements to my solution. One is to include a reference to the initiating element with the AJAX call. The second is to define an event which can be triggered on that element and used to pass back the response data. So instead of the above example our call will look more like this:

commentMethods.create({data}, $this, 'response.createComment');

The data argument is an object containing everything we need to actually generate the comment such as the comment text. $this refers to the element making the call, our 'add comment' button and the third argument is a string which is the name of the custom event we'll use to handle the response.

Now that we know what we're passing to the method let's look at the method itself. For the most part this is a straight forward jQuery AJAX call. The interesting part is in the success function where you can see we trigger the custom event on our button. Not only does that event trigger a response, but it also passes along the data we received back from the web service.

var commentMethods = (function($){
    var methods = {
        create: function (data, eventTarget, eventType) {
                url: '/CommentsWebService.svc/addComment',
                type: 'POST',
                contentType: "application/json; charset=utf-8",
                data: JSON.stringify(data),
                dataType: 'json',
                success: function (data) {
                    //on success trigger the custom event on
                    //the eventTarget (add comment button)
                    //this is what triggers the response
                    //handler and passes the data
                    eventTarget.trigger(eventType, [data]);
                error: function (jqXHR, status, error) {
                    //if there was an error actually calling
                    //the webs ervice we build our own response
                    //object and pass that back
                    var data = {
                        'ErrorMessage': 'Error',
                        'Success': false
                    eventTarget.trigger(eventType, [data]);                       

    return {
        create:    methods.create

} (jQuery));

Of course this means our button will have two events bound to it. One to handle the click by the user and a second to handle the response from the method defined above.

//bind your button events
elements.body.delegate('.addComment', 'click', handleAddComment);
elements.body.delegate('.addComment', 'response.createComment', handleAddCommentResponse);

A few notes about the code above:

  • elements refers to an object which was created when the script was initialized containing references to DOM elements which will be referred to repeatedly including the body (ex: elements.body = $("body"))
  • delegate is used here to bind a single event to the body instead of binding duplicate events to every .addComment button on the page

The function, handleAddComment, is the one run when the users click the 'add comment' button. It collects the required data and calls our comment creation method.

handleAddComment (event) {
    //declare your variables
    var $this, data, commentText, subjectGuid;
    //get the button element (which button was clicked?)
    $this = $(event.currentTarget);
    //get the comment text (what does the comment say?)
    commentText = $this.siblings('.commentInput').val();
    //get the subject guid (which item is the comment about?)
    subjectId = $this.parents('.contentItem').attr('data-guid');
    //create your data 
data = { commentText: commentText, subjectGuid: subjectGuid };

//pass data to the method which will create the comment by //calling a web service method commentMethods.create(data, $this, 'response.createComment'); }

Once that has run we're passively waiting for a response from the web service which will come in the form of our custom event being being triggered on our element. The following function is bound to that event and will handle the response.

handleCreateCommentResponse (event, data) {
    //actions common to both a success and error response 
    //could be placed here such as hiding a spinner
    if (data.Success) {
        handleCreateCommentSuccess(event, data);
    } else {
        handleCreateCommentError(event, data);

It expects a data object like this:

    Success: true,
    ErrorMessage: "",
    Html: "<.... />"

You can imagine what might happen in the error handling function (displaying an error, reverting changes which weren't saved etc.) Here is a quick example of what the success function might look like:

handleCreateCommentResponse (event, data) {
    //declare your variables
    var $this;
    //get your button
    $this = $(event.currentTarget);
    //replace the current comment list with the response HTML
    //clear the text from the comment input

That's pretty much it. It is a flexible and repeatable pattern which allows a common structure to be used for a variety of AJAX operations.

If you were to expand the methods file included earlier it would contain all the methods needed to deal with comments. These are the methods my comments.methods.js file provided:

commentMethods.create(data, $this, 'response.createComment');
commentMethods.update(data, $this, 'response.createComment');
commentMethods.delete(data, $this, 'response.createComment');

And there is the events file which handles all the events for your page, or by function, depending on how separated you like your code to be. I usually like to have all the code for one page or page type in a single file unless you have duplicate functionality on multiple pages then I'd certainly group the functionality to avoid repetition. That's an organizational decision you'd have to make based on the project and your preferences.

This is a quick example of the event handlers my comments.js contained:

handleAddComment () { ... }
handleAddCommentResponse () { ... }
handleAddCommentSuccess () { ... }
handleAddCommentError () { ... }

handleUpdateComment () { ... }
handleUpdateCommentResponse () { ... }
handleUpdateCommentSuccess () { ... }
handleUpdateCommentError () { ... }

handleDeleteComment () { ... }
handleDeleteCommentResponse () { ... }
handleDeleteCommentSuccess () { ... }
handleDeleteCommentError () { ... }

In hindsight this project was an obvious candidate for the use of a javascript MVC framework such as AngularJS, Backbone, or Sproutcore. Why were these technologies not leveraged? There are a few reasons. A fast moving project, uncertainty about how they would mesh with SharePoint, and not realizing just how useful they could have been.

There is also the possibility of using a plugin architecture for this type of work but I've found the above approach offers greater flexibility and makes it easier to keep code organized according to where it is used and not exclusively by function. Another problem with plugins and dynamic applications such as this the need to invoke plugins as new content is loaded inline and all the additional event binding it requires.

In conclusion, I think the solution I did use, while certainly imperfect is pretty decent and would be useful when a framework cannot be used or the scope of the work does not warrant it.

So let me know what you think. I'm eager to hear if others have used a similar approach or what a better solution might be.