Here is a way to take advantage of the Content-Type
returned by each respective result. We are using this to send back error information, and there's already JS in place to display messages, so we get either the partial view we want, or control information for error reporting etc.
For reference, a partial view returns text/html
and a JSON response should return application/json
.
As usual, the fun part is on the javascript side, and the JQuery ajax()
does not disappoint here!
In your controller, just return either PartialView()
or Json(model,)
as appropriate; we are using it in try/catch
format.
public ActionResult Edit(int id) {
try {
var model = getYourModel();
return PartialView("Edit", model);
}
catch (Exception ex) {
var mi = new MessageInfo(MessageType.Error, String.Format("Edit failed: {0}", ex.Message), true);
return Json(mi, "application/json", JsonRequestBehavior.AllowGet);
}
}
On the JS side, we are using the following function. Note that you need to re-establish any JQuery events you hooked in $(document).ready()
from the initial page-level GET, so we have a callback parameter.
function getPartialView(action, controller, model, divId, callback) {
var url = "/" + controller + "/" + action + "/";
$.ajax({
type: "GET",
url: url,
data: model,
success: function (data, textStatus, jqXHR) {
var ct = jqXHR.getResponseHeader("Content-Type");
var mx = ct.match("text\/html");
if (mx != null) {
$(divId).html(data);
if (callback) {
callback($(divId));
}
}
else {
addMessage(data.type, data.title, data.text, data.sticky);
}
},
error: function (jqXHR, textStatus, errorThrown) {
addMessage(3, "\"" + url + "\": Failed", textStatus + ": " + errorThrown, false);
}
});
}
The only tricky bit is checking the Content-Type
header in the response, and behaving accordingly. Please note we are "cheating" by assuming JSON if it wasn't HTML. We are calling our pre-existing addMessage()
function, do whatever you need!
And finally, here is a sample Anchor element with onclick
targeting getPartialView()
above.
<a href="#" onclick="getPartialView('Action', 'Controller', model, '#partialviewdivid', function(dvx) { connectJqueryEvents(dvx); })">Cancel</a>
Works Great...
Except for form submits via Ajax.BeginForm()
where the JSON payload is treated mistakenly as HTML due to insufficient validation of Content-Type
. The result is your div
gets some JSON added to it, which basically does not render as HTML. The AjaxOptions.OnSuccess
callback does execute, but it's too late for your DOM at that point!
There is a simple solution, but unfortunately it requires a small repair to jquery-unobtrusive-ajax.js
because the asyncOnSuccess()
function was short-sighted as written.
function asyncOnSuccess(element, data, contentType) {
var mode;
if (contentType.indexOf("application/x-javascript") !== -1) {
return;
}
if (contentType.indexOf("application/json") !== -1) {
return;
}
...snip...
}
In the OOTB version, the second if
statement is missing; adding it is the fix necessary to keep it from slamming your JSON payload into the DOM.
With this fix in place, the JSON payload passes into your AjaxOptions.OnSuccess
Javascript, and you can proceed as necessary.
Yes You Can Get Both
Hopefully you know since you are sending Json, you could send back any kind of model, and let the Javascript sort it out; hasOwnProperty()
comes in handy there. So obviously you can send back some view HTML via already-mentioned RenderViewToString()
.