Experience Sitecore ! | All posts tagged 'Ajax'

Experience Sitecore !

More than 200 articles about the best DXP by Martin Miles

Editing content on a CD server. Part 1. MVC ajax request to controller

Imagine the situation, when you need to have a page with an updatable text, for instance:

This div becomes editable as you click it

Now the next logical step would be to fire on blur client event (it happens when out focus out of div, ending the editing mode) and send changed content somewhere to the back end. Something simple like jQuery snippet below can handle that:


$('#editField').blur(function () {
        
    $.ajax({
        url: 'some/backend/url/to/post',
        data: { name: value },
        type: 'post',
        success: function () {
            // handle success 
        },
        error: function () {
            // handle error
        }
    });
});

So far, so good. The very next question would be - how do I create an endpoint in Sitecore to support that ajax post request and how do I pass the data and handle positive and negative outcomes? I assume, the back end should have some MVC controller action, that does some back end job of storing my data and returning JSON object back to client script.

So in order to make this work we register MVC routes, this is referenced from Application_Start event handler and is usually implemented in App_Start folder.
    public class Application : Sitecore.Web.Application
    {
        protected void Application_Start()
        {
            RouteConfig.RegisterRoutes(RouteTable.Routes);
        }
    }
    public class RouteConfig
    {
        public static void RegisterRoutes(RouteCollection routes)
        {
            routes.MapRoute(
                 name: "ajax",
                 url: "api/Ajax/{action}/{id}",
                 defaults: new { controller = "Ajax", 
                                 action = "DefaultActionMethodName", id = UrlParameter.Optional }
               );
        }
    }

This route binds all the /api/Ajax requests to be served by AjaxController class. But there is one more setting you require to do in order for your request to go the right direction - in config file set up a custom handler that will intercept that types of requests:

  
    ...
    
    ...

Controller action method being called is specified by caller, and will call DefaultActionMethodName as a default fallback if missing, passing id is optional. Here is the controller:

    public class AjaxController : Controller
    {
        [HttpPost]
        public ActionResult PostComment(string id, PostCommentViewModel model)
        {
            // implement backend logic here

            return Json();
        }
    }

 Controller accepts id as a parameter automatically resolved from URL, as specified in route we configured earlier. It also accepts and binds JSON object that we send as data into PostCommentViewModel object that automatically comes into controller as second parameter. Here is its implementation:

    public class PostCommentViewModel
    {
        [AllowHtml]
        public string Comment { get; set; }
    }
So, we can now finalize jQuery snippet that handles blur effect and sends data to controller. I have intentionally simplified it using external JavaScript objects, for clarity of understanding, you would normally avoid using global JavaScript variables in production code. These objects are used to keep state between ajax calls and to call server only when content is modified indeed. 
If there was an error on server, script retains previous value. If request worked out successfully with a status code 200 (OK) the we store updated value into <div> tag.  
var contents = $('#editField').html();
var id = '@Html.Sitecore().CurrentItem.ID';

var data = {};

$('#editField').blur(function () {
    if (contents != $(this).html()) {

        $.ajax({
            url: '/api/Ajax/PostComment/' + id,
            data: { Comment: $('#editField').html().trim() },
            type: 'post',
            success: function () {
                contents = $('#editField').html();
                var k = 0;
            },
            error: function () {
                $(this).html(contents);
            }
        });

        contents = $(this).html();
    }
});

On the server side there is not much to do with it - just save to database and return the result. Here is the final code of PostComment action of AjaxController:

 public class AjaxController : BaseController
    {
        [HttpPost]
        public ActionResult PostComment(string id, PostCommentViewModel model) // change to HtmlString
        {
            //Response.StatusCode = 500; 

            Database database = Sitecore.Context.Database;
            var item = database.GetItem(id);

            using (new Sitecore.SecurityModel.SecurityDisabler())
            {
                item.Editing.BeginEdit();
                try
                {
                    item.Fields["Comment"].Value = model.Comment;
                }
                finally
                {
                    item.Editing.EndEdit();
                }
            }

            item = database.GetItem(id);
            var val = item.Fields["Comment"].Value;

            return Json(id + " |" + model.Comment);
        }
    }