Partial Views vs. Json (or both)

前端 未结 4 1385
长情又很酷
长情又很酷 2020-12-13 07:16

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. The

相关标签:
4条回答
  • 2020-12-13 07:39

    You can do this way as well, Put this Inside your controller.

    protected string RenderPartialViewToString(string viewName, object model)
    {
        if (string.IsNullOrEmpty(viewName))
            viewName = ControllerContext.RouteData.GetRequiredString("action");
    
        ViewData.Model = model;
    
        using (StringWriter sw = new StringWriter()) {
            ViewEngineResult viewResult = ViewEngines.Engines.FindPartialView(ControllerContext, viewName);
            ViewContext viewContext = new ViewContext(ControllerContext, viewResult.View, ViewData, TempData, sw);
            viewResult.View.Render(viewContext, sw);
    
            return sw.GetStringBuilder().ToString();
        }
    }
    
    0 讨论(0)
  • 2020-12-13 07:40

    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

    0 讨论(0)
  • 2020-12-13 07:53

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

    0 讨论(0)
  • 2020-12-13 08:00

    I believe you could return the rendered html as a string - this could alternately be an html string containing an error message to display?

    0 讨论(0)
提交回复
热议问题