I use ASP.NET MVC with jQuery and have a lot of Ajax requests to my controllers.
Use Partial Views (usercontrols) to build the intial view when a page is loaded. Then if I need to append/replace data based on my Ajax request I build HTML from the Json response.
This approach gives me full control, ie. I can get extra information back from my controller if something went wrong, and then show an error message based on that.
However, recently I've been really annoyed with all the extra work that goes into maintaining HTML structure both in my partial views and the part that generates HTML from Json.
I like to make a jQuery ajax request and then have the controller return PartialView("mypartialview") and then just use jQuery to replace to HTML in the view.
However, this way I cannot attach extra data from the controller - it's either whatever the partial view gives me - or nothing. At least that's my current take on it.
If some validation goes wrong at some point in my controller action I don't want to return the HTML of the partial view.
So how do you go about handling this issue?
Thanks for reading.
Partial views are an effective way to: Break up large markup files into smaller components. In a large, complex markup file composed of several logical pieces, there's an advantage to working with each piece isolated into a partial view.
Views are the general result of a page that results in a display. It's the highest level container except the masterpage. While a partial view is for a small piece of content that may be reused on different pages, or multiple times in a page.
Thank you for answer but there is no multiple partial.
Advantages of Partial View in ASP.net MVC: using Partial View in ASP.NET MVC has following advantages: Enhances reusability by packaging up common website code instead of repeating the same in different pages. Easy to maintain. Changes in future are simple to accommodate.
Based on this stackoverflow anwser I have just set out to do the same thing.
First create an extension method for the controller class.
public static string RenderViewToString(this Controller controller, string viewName, object model)
{
using (var writer = new StringWriter())
{
var viewResult = ViewEngines.Engines.FindPartialView(controller.ControllerContext, viewName);
controller.ViewData.Model = model;
var viewCxt = new ViewContext(controller.ControllerContext, viewResult.View, controller.ViewData, controller.TempData, writer);
viewCxt.View.Render(viewCxt, writer);
return writer.ToString();
}
}
Then return the json in the controllers action method.
return Json(new {
Html = this.RenderViewToString("MyView", model),
SomeExtraData = data
});
Your ajax requests will now receive json with the html contained in it. Still experimenting with this approach over returning plain Html fragments.
Hope that helps.
EDIT Updated to work with razor
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()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With