2009-12-10 7 views
9

Esto es una continuación de un previous question que tuve antes sobre pasar un error al cliente, pero también pertenece a ModelState.En ASP.Net MVC, ¿se puede usar ModelState con una actualización de Ajax?

¿Alguien ha utilizado con éxito el enfoque Cena Nerd, pero con Ajax? Así que Nerd Dinner hace una actualización como tal.

[AcceptVerbs(HttpVerbs.Post)] 
    public ActionResult Edit(int id, FormCollection formValues) 
{ 
    Dinner dinner = dinnerRepository.GetDinner(id); 
    try 
    { 
     UpdateModel(dinner); 
     dinnerRepository.Save(); 
     return RedirectToAction("Details", new { id=dinner.DinnerID }); 
    } 
    catch 
    { 
     foreach (var issue in dinner.GetRuleViolations()) { 
     ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage); 
    } 
     return View(dinner); 
    } 
} 

Usando jQuery $ .ajax

function hijack(form, callback, errorFunction, format) { 
    $.ajax({ 
     url: form.action, 
     type: form.method, 
     dataType: format, 
     data: $(form).serialize(), 
     success: callback, 
     error: function(xhr, textStatus, errorThrown) { 
      errorFunction(xhr, textStatus, errorThrown); 
     } 
    }); 
} 

Ajax, la parte de "probar" del controlador se convierte en

try 
{ 
    UpdateModel(dinner); 
    dinnerRepository.Save(); 
    return PartialView("PartialDetails", new { id=dinner.DinnerID }); 
} 

, pero ¿qué hacer acerca de la parte de captura?

Una solución simple de gestión de errores para devolver un error sería

catch(Exception ex) 
{ 
    Response.StatusCode = 500;     
    return Content("An Error occured."); 
    //throw ex; 
} 

, pero que no pasa a través de la robusta ModelState integrado en MVC. Pensé en una serie de opciones, pero realmente quiero 2 cosas:

  1. Quiero que el error se maneje en el atributo de error de jQuery.
  2. Quiero utilizar la lógica de validación de MVC de ASP.Net tanto como sea posible.

¿Esto es posible? Si no, ¿cuáles son las mejores alternativas que conoces?

Muchas gracias.

Actualización No he marcado esto como respondido aún, porque aún no he implementado lo que creo que funcionará mejor.

He decidido que realmente no me gusta el éxito => enviar lista actualizada, falla => enviar el enfoque de mensaje de error que estaba tomando. Hice esto para reducir el número de llamadas, pero realmente se está estableciendo una lista actualizada en la página. Intentar hacer ambas cosas ajusta fuertemente la ventana emergente a su página general.

Voy a agregar un evento jQuery personalizado para actualizar la lista de la página maestra cuando se cierra el cuadro de diálogo. En esencia, es el patrón del observador. Me gusta la idea de que la página diga a la ventana emergente "dime cuando hayas terminado" (también conocido como cerrado), sin tener que decirle a la ventana emergente por qué. Requiere una llamada adicional, pero no lo veo como un gran problema.

Todavía no estoy seguro de lo bien que me gusta/no me gusta la validación del lado del servidor y estoy considerando ir con la validación solo del lado del cliente. Mientras que la validación del lado del servidor parece una capa limpia, también tiene una serie de problemas, entre ellos:

1) Pone controles de calidad al final, en lugar de al principio. Una analogía con la fabricación sería un automóvil que se prueba cuando llega al distribuidor, en lugar de los puntos en el proceso donde se está construyendo.
2) Viola la intención de Ajax. Ajax no solo se trata de enviar eventos asincrónicos, también se trata de enviar solo lo que necesito y recibir solo lo que necesito. Devolver todo el estado del modelo para proporcionar detalles de error no parece ir con Ajax.

Lo que estoy pensando hacer es tener una validación solo del lado del cliente, pero ese código de servidor y un modelo de vista personalizado se pueden usar para decirle al cliente cómo crear dinámicamente esas reglas de validación.

También sospecho que un lenguaje dinámico como IronRuby o IronPython podría ofrecer una forma más elegante de resolver estos problemas, pero podría pasar un poco más de tiempo antes de considerar esa posibilidad.

+0

Bueno, yo creo que realmente depende de la situación, si al hacerlo 2 petición no es un problema que iré para eso.Personalmente, hacer muchas validaciones con JavaScript en el lado del cliente no es algo que me guste (no sé por qué, pero veo js como algo no muy confiable/seguro/igualmente implementado (en este jquery salve el día)), especialmente porque a veces simplemente no puedes hacer todas las validaciones en el lado del cliente, debes usar algún tipo de verificaciones secundarias del servidor, como (¿esta entidad ya existe en DB?), y admite clientes deshabilitados, pero como dije al principio depende del escenario – JOBG

+0

Acepto que no se puede hacer toda la validación por parte del cliente, simplemente parece que el marco de validación parece orientarse principalmente hacia errores de campo (demasiado largos, no una fecha, etc.). Al menos en los ejemplos que he visto. Cuando revisa la entrada del usuario, Javascript parece ser el lugar apropiado para realizar la comprobación. – John

+0

He confirmado mi decisión de hacer 2 llamadas, en lugar de una, porque desvincula la vista parcial de detalles de la vista de página debajo de ella. Quiero que la ventana emergente diga de nuevo a la vista de la página "Estoy cerrado", pero no sé nada sobre lo que la vista de la página va a hacer. jQuery debería hacer eso posible sin mucho esfuerzo. Eso me permite no preocuparme de que los cambios en mi vista parcial de los detalles afectarán a la página y viceversa. – John

Respuesta

4

Si yo entiendo bien lo que está tratando de hacer, mi primera respuesta será no, no se puede utilizar el modelo de estado, ya que es a través y petición Ajax. Tal vez se puede emular el comportamiento ModelState, para mostrar los errores:

  1. Expulsar una (propiedad, mensaje) List<KeyValuePair<string,string>> por JSON (esto requerirá que para pasar los modelErrors forman la ModelState a la nueva estructura) y hacer la construcción de HTML de un Resumen de Validación por JS/jQuery (que creo que es una solución para matar).

  2. Si van al servidor, y hay algún error Solo hacer un render parcial de la Html.ValidationSummary(), pasarla a través de JSON y anteponer a la forma. Si todo estaba bien, solo devuelva la vista Detalles parciales y reemplace el contenido real. Esto requerirá algún tipo de parámetro de estado para que sepa qué devuelve el servidor en la devolución de llamada ajax.

EDIT: Esta última opción suena bien, pero complicado, ya que necesitará para devolver una vista parcial en una forma de cadena por JsonResult, aquí hay una pregunta y solución de eso hackear Render a view as a string.

Personalmente, no creo que el uso del atributo de error hará ningún bien en absoluto, yo sólo lo uso a situaciones muy específicas, como los errores de tiempo de espera, y excepciones del servidor de aplicaciones, no excepciones.

EDIT: Utilizando JSON

[AcceptVerbs(HttpVerbs.Post)] 
     public ActionResult Edit(int id, FormCollection formValues) 
     { 
      Dinner dinner = dinnerRepository.GetDinner(id); 
      try 
      { 
       UpdateModel(dinner); 
       dinnerRepository.Save(); 
       return Json(new 
       { 
        result = "success", 
        html = this.RenderToString("PartialDetails", dinner) 
       }); 

      } 
      catch 
      { 
       foreach (var issue in dinner.GetRuleViolations()) 
       { 
        ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage); 
       } 
       return Json(new 
       { 
        result = "failed", 
        html = this.RenderToString("PartialEdit", dinner) 
       }); 
      } 
     } 

Aquí el parámetro de resultado le permitirá saber qué acción hay que hacer en cada caso, sólo hay que comprobarlo en la devolución de llamada.

+0

Gracias. Parte del desafío, sin embargo, es que preferiría enviar los resultados de regreso como resultado de PartialView y no como un resultado de JSON. Para 2., ¿estás haciendo 2 llamadas? Uno para devolver el éxito/error y otro para entregar los resultados? Esa es una opción en la que estaba pensando. – John

+0

Es realmente una llamada con esta estructura como {"estado": [{"código": "error"}], "html": [{"html": "un montón de html"}]}. Si desea que sea solo un objeto de Resultado parcial, ¿por qué no devuelve el agujero Editar vista parcial, en caso de que el modelo tenga errores? y la vista parcial Detalles cuando no se producen errores, el estado del modelo funcionará sin problemas de esa manera. – JOBG

+0

La parte que hace que mi escenario sea diferente de la mayoría de los ejemplos es que quiero hacer dos acciones muy diferentes en función de si es un éxito o un error. Si es un error, puedo devolver la Vista parcial de edición y volver a crear el cuadro de diálogo. Sin embargo, si es un éxito, quiero cerrar el diálogo y actualizar la lista en la página principal. Entonces, el código del lado del cliente necesita saber si es un éxito/falla para saber qué objeto actualizar: el diálogo o la lista. – John

2

Adición de mi enfoque preferido con MVC3. Utilizando el método de edición que podría hacer algo como

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Edit(int id, FormCollection formValues) 
{ 
    Dinner dinner = dinnerRepository.GetDinner(id); 
    try 
    { 
     UpdateModel(dinner); 
     dinnerRepository.Save(); 
     return Json (new { Success = true, Url = Url.Action("DetailsPartial", dinner), Div = "#DivToUpdateId" }); 
    } 
    catch 
    { 
     foreach (var issue in dinner.GetRuleViolations()) { 
     ModelState.AddModelError(issue.PropertyName, issue.ErrorMessage); 
    } 
     //I am replacing this with a partial view which will contain the model state errors. For people using MVC 3 with razor they should be able to use their normal views as partials 
     return PartialView(dinner); 
    } 
} 

y luego se puede utilizar como una función del éxito

success: function(data) { 
    if (data.Success) { 
     $.post(data.Url, function(partial) { 
      $(data.Div).html(partial); 
     }); 
    } 
    else 
    { 
     $('#formDiv').html(data) 
    } 
} 

lo que, básicamente, si el resultado es JSON a continuación data.Success es cierto. Luego actualiza el div especificado en json (data.Div) con los resultados de la acción especificada en la Url. Si el resultado no es Json es porque el método de publicación falló y el formDiv se actualiza con el formulario parcial que contiene los errores de ModelState. Este es el enfoque que uso para los diálogos, pero funciona bastante bien. Obviamente, con MVC3 cambiaría parte del método de edición, como usar TryUpdateModel, etc., pero solo intento dar un ejemplo de mi enfoque. Al pasar la url y publicar los resultados del método, no hay necesidad de tratar de convertir html a una cadena para pasar un parcial.

+0

Interesante. ¿Sería capaz de publicar su código para Editar PartialView? – John

Cuestiones relacionadas