Thursday, August 21, 2014

Sitecore MVC and Ajax.BeginForm: How to replace whole view with received HTML code

Ajax.BeginForm easily allows you to work add HTML code returned by view to page. It is easy to do by defining Ajax option UpdateTargetId

UpdateTargetId =
"DivContainer"
But there are only three options how received HTML content should be placed to view (InsertionMode AjaxOption):
  1. InsertionMode.InsertAfter
  2. InsertionMode.InsertBefore
  3. InsertionMode.Replace
Unfortunately, even InsertionMode.Replace mode don’t replace whole HTML element with new code, it replaces only content of this element

$(update).html(data); //jquery.unobstrusive-ajax.js
With ASP.Net MVC it is ok. You can control place where you are rendering your partial view:
<div id="DivContainer">
    @Html.Action("Action","Controller")
</div>
Then you are able to set UpdateTargetId = "DivContainer" and your form will be completely replaced.
BUT! It is not possible to do with Sitecore MVC when your view is added to some placeholder on page. Even if you wrap your view with some element
<div id="DivContainer">
@using (Ajax.BeginForm("Action", "Controller", null, new AjaxOptions {      UpdateTargetId = "DivContainer", InsertionMode = InsertionMode.Replace ….      
</div>
you’ll get a lot of wrapped divs after few updating of form:
<div id="DivContainer">
<div id="DivContainer">
<div id="DivContainer">
@using (Ajax.BeginForm("Action", "Controller", null, new AjaxOptions {      UpdateTargetId = "DivContainer", InsertionMode = InsertionMode.Replace ….      
</div>
</div>
</div>
It can break your design of JavaScript on page.
There is one solution how you are able to avoid it. Insertion HTML received by Ajax is controlled by jquery.unobstrusive-ajax.js. It is JavaScript file and you can easily modify it. For Sitecore MVC views I suggest to add additional insertion mode:
mode = (element.getAttribute("data-ajax-mode") || "").toUpperCase();
        $(element.getAttribute("data-ajax-update")).each(function (i, update) {
            var top;
            switch (mode) {
            case "BEFORE":
                top = update.firstChild;
                $("<div />").html(data).contents().each(function () {
                    update.insertBefore(this, top);
                });
                break;
            case "AFTER":
                $("<div />").html(data).contents().each(function () {
                    update.appendChild(this);
                });
                break;
            case "REPLACEWITH":
                $(update).replaceWith(data);
                break;
            default:
                $(update).html(data);
                break;
            }
        });

It will replace whole HTML element, but not only content of it.
And also you should pass corresponding html attribute to your form:
@using (Ajax.BeginForm("Action", "Controller", null, new AjaxOptions {
UpdateTargetId = "DivContainer"}new { enctype = "multipart/form-data", data_ajax_mode = "replacewith" }
Now you can do not worry about nested containers after Ajax updating of views on your Sitecore MVC application.
For future: I plan to implement my own  Ajax.BeginForm helper with similar abilities targeted on Sitecore.

No comments:

Post a Comment