Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Partial Views vs. Json (or both)

Tags:

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.

like image 264
bgeek Avatar asked Sep 24 '09 11:09

bgeek


People also ask

When should partial views be used?

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.

What is difference between view and partial view in MVC?

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.

Can we use multiple partial view in MVC?

Thank you for answer but there is no multiple partial.

What is advantage of partial view in MVC?

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.


2 Answers

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

like image 95
Magpie Avatar answered Oct 12 '22 22:10

Magpie


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().

like image 26
escape-llc Avatar answered Oct 12 '22 22:10

escape-llc