2009-09-24 28 views
19

Uso ASP.NET MVC con jQuery y tengo muchas solicitudes Ajax para mis controladores.Vistas parciales frente a Json (o ambas)

Utilice vistas parciales (controles de usuario) para construir la vista inicial cuando se carga una página. Entonces, si necesito anexar/reemplazar datos en función de mi solicitud de Ajax, creo HTML a partir de la respuesta de Json.

Este enfoque me da un control total, es decir. Puedo obtener información adicional de mi controlador si algo salió mal, y luego mostrar un mensaje de error basado en eso.

Sin embargo, recientemente me he sentido realmente molesto con todo el trabajo adicional que implica mantener la estructura HTML tanto en mis vistas parciales como en la parte que genera HTML de Json.

Me gusta hacer una solicitud jayue ajax y luego hacer que el controlador devuelva PartialView ("mypartialview") y luego simplemente usar jQuery para reemplazar a HTML en la vista.

Sin embargo, de esta manera no puedo adjuntar datos adicionales del controlador, es lo que sea que me dé la vista parcial, o nada. Al menos esa es mi opinión actual.

Si alguna validación falla en algún momento de la acción de mi controlador, no deseo devolver el código HTML de la vista parcial.

Entonces, ¿cómo manejas este problema?

Gracias por leer.

Respuesta

2

Creo que podría devolver el html representado como una cadena - ¿podría ser alternativamente una cadena html que contenga un mensaje de error para mostrar?

+1

De hecho, eso es lo que es una respuesta ParcialView. Y aunque no lo deletree del todo, esta es la solución: haga que sus métodos AJAX devuelvan un ParialView renderizado utilizando el mismo control de usuario que utilizó para representar inicialmente esa parte de la página. Para ello, escriba return PartialView (modelo) en lugar de return Json (modelo). –

+0

Gracias. Esa es de hecho una mejor explicación :) – Paddy

+0

Craig, eso es lo que hago ahora. Mi problema es que quiero devolver tanto Html como Json, o dicho de otro modo: quiero que el html resultante devuelto por PartialView devuelto y luego envuelto en Json, para poder enviar otros datos también. Algo así como este tipo: http://stackoverflow.com/questions/1168791/returning-a-rentered-html-partial-in-a-json-property-in-asp-net-mvc supongo lo anterior funcionaría, pero me gustaría saber cómo otros manejan esto. – bgeek

18

Basado en this stackoverflow awser Acabo de hacer lo mismo.

Primero cree un método de extensión para la clase de controlador.

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(); 
    } 
} 

A continuación, devuelva el json en el método de acción de los controladores.

return Json(new 
          { 
           Html = this.RenderViewToString("MyView", model), 
           SomeExtraData = data 
          }); 

Sus solicitudes de ajax ahora recibirán json con el html contenido en él. Todavía estoy experimentando con este enfoque sobre devolver fragmentos Html simples.

Espero que ayude.

EDITAR actualizado para trabajar con la maquinilla de afeitar

4

Aquí es una manera de tomar ventaja de la Content-Type devuelto por cada resultado respectivo. Estamos utilizando esto para enviar información de error, y ya hay JS en su lugar para mostrar mensajes, por lo que obtenemos la vista parcial que queremos o control de información para informes de errores, etc.

Como referencia, una vista parcial devuelve text/html y una respuesta JSON debería devolver application/json.

Como de costumbre, la parte divertida está en el lado de Javascript, y el JQuery ajax() no decepciona aquí!

En su controlador, simplemente devuelva PartialView() o Json(model,) según corresponda; lo estamos usando en el formato try/catch.

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); 
    } 
} 

En el lado JS, estamos utilizando la siguiente función. Tenga en cuenta que debe volver a establecer cualquier evento de JQuery enganchado en $(document).ready() desde el nivel de página inicial GET, por lo que tenemos un parámetro de devolución de llamada.

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); 
     } 
    }); 
} 

El único poco complicado está comprobando la cabecera Content-Type en la respuesta, y de actuar en consecuencia. Tenga en cuenta que estamos "haciendo trampa" al asumir JSON si no fuera HTML. Estamos llamando a nuestra función preexistente addMessage(), haga lo que necesite!

Y, por último, he aquí un ejemplo de elemento de ancla con onclick que apunta a getPartialView() arriba.

<a href="#" onclick="getPartialView('Action', 'Controller', model, '#partialviewdivid', function(dvx) { connectJqueryEvents(dvx); })">Cancel</a> 

Obras Gran ...

excepción de formulario envía a través de Ajax.BeginForm() donde la carga útil JSON es tratada erróneamente como HTML debido a la insuficiente validación de Content-Type. El resultado es que su div obtiene un poco de JSON, que básicamente no se procesa como HTML. La devolución de llamada AjaxOptions.OnSuccess se ejecuta, pero es demasiado tarde para su DOM en ese momento.

hay una solución sencilla, pero por desgracia, requiere una pequeña reparación a jquery-unobtrusive-ajax.js porque la función asyncOnSuccess() era miope como está escrito.

function asyncOnSuccess(element, data, contentType) { 
    var mode; 

    if (contentType.indexOf("application/x-javascript") !== -1) { 
    return; 
    } 
    if (contentType.indexOf("application/json") !== -1) { 
    return; 
    } 
...snip... 
} 

En la versión OOTB, la segunda if declaración no se encuentra; añadiendo que es la solución necesaria para evitar que golpee su carga JSON en el DOM.

Con esta corrección en su lugar, la carga JSON pasa a su AjaxOptions.OnSuccess Javascript, y puede continuar si es necesario.

Sí Usted puede obtener tanto

Esperamos que usted sabe ya que usted está enviando JSON, que podría devolver cualquier tipo de modelo, y dejar que el Javascript a solucionar el problema; hasOwnProperty() es muy útil allí. Así que, obviamente, puede enviar de vuelta algo de HTML de vista a través del ya mencionado RenderViewToString().

0

También puede hacerlo de esta manera, Ponga esto dentro de su controlador.

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(); 
    } 
} 
Cuestiones relacionadas