2012-02-09 11 views
5

En mi modelo de vista, tengo una lista de elementos que obtengo de la base de datos y luego los envío a la vista. Me gustaría saber si es posible evitar tener que volver a llenar la propiedad de las opciones cada vez que pulso una acción de publicación y necesito devolver el modelo (para los errores de validación y para qué no).Reutilizar los datos del modelo en una acción posterior

En formularios web, esto no sería necesario.

Editar: No estaba claro. Mi problema es con las opciones de SelectList que uso para DropDownLists. Todo se publica, pero si tengo que regresar a la vista (el modelo no es válido), ¡tengo que volver a cargar las opciones de la base de datos! Quiero saber si esto puede evitarse.

Mi modelo de vista:

public class TestModel 
{ 
    public TestModel() 
    { 
     Departments = new List<SelectListItem>(); 
    } 

    public string Name { get; set; } 
    public int Department { get; set; } 
    public IEnumerable<SelectListItem> Departments { get; set; } 
} 

Mi opinión:

@model MvcApplication1.Models.TestModel  
@using (Html.BeginForm()) 
{ 
    @Html.TextBoxFor(m => m.Name) 

    @Html.DropDownListFor(m => m.Department, Model.Departments) 

    <input type=submit value=Submit /> 
} 

Mi controlador (no notar el comentario sobre HttpPost):

public ActionResult Index() 
{ 
    TestModel model = new TestModel 
    { 
     Name = "Rafael", 
     Department = 1, 
     Departments = new List<SelectListItem> 
     { 
      new SelectListItem { Text = "Sales", Value = "1" }, 
      new SelectListItem { Text = "Marketing", Value = "2", Selected = true }, 
      new SelectListItem { Text = "Development", Value = "3" } 
     } 
    }; 

    // Departments gets filled from a database. 

    return View(model); 
} 

[HttpPost] 
public ActionResult Index(TestModel model) 
{ 
if (!ModelState.IsValid) 
{ 
    //Do I have to fill model.Departments again!?!?!? 

    return View(model); 
} 
else { ... } 
} 

Gracias de antemano.

Edit: FYI, mi solución fue utilizar la variable Session.

Respuesta

1

Solo necesita escribir su vista fuertemente, y cambie su método de controlador para tener un parámetro de ese tipo de clase.

Es decir, la vista

@model MyNamesspace.Models.MyModel 
... 
@using (Html.BeginForm()) 
{ 
    .... 
} 

Y controlador método que se ha publicado en.

[HttpPost] 
public ActionResult MyAction(MyModel model) 
{ 
    ... 
} 

EDITAR: También asegúrese de tener campos de formulario para cada propiedad del modelo que necesita publicar en el controlador. Mi ejemplo es usar Razor también por cierto.

+0

¿Me puede ver editar? – rebelliard

+0

OK, veo lo que quieres decir ahora. Tuve el mismo problema con mi último proyecto. Lamentablemente, usar una publicación HTTP limita de forma inherente su modelo al equivalente de pares clave-valor. Es decir, no puede tener un modelo complejo con más objetos comerciales como propiedades devueltas al controlador. Por lo tanto, deberá almacenar la lista de departamentos en algún lugar persistente. La sesión sería apropiada para esto, siempre y cuando la borre cuando haya terminado. Si realmente necesita enviar objetos por cable, debe usar JSON o alguna otra técnica de serialización. – jhsowter

+0

Ya veo. ¿Recomendaría pasar los datos de un JSON dentro de una entrada oculta o un objeto Session []? Gracias. – rebelliard

0

Encontré un problema similar al intentar crear un asistente Order en MVC (uno donde cada página del asistente se implementa como una vista parcial cargada por AJAX). Lo dudo mucho es la sugirió método, pero mi manera de resolver esto fue para llamar a un método personalizado MergeChanges en cada acción llamada por mi asistente:

public Order MergeChanges(Order newOrder) 
{ 
    var sessionHistory = (List<string>)Session["sessionHistory"]; 

    if (sessionHistory == null || sessionHistory.Count == 0) 
    return MergeChanges(newOrder, -1); 

    return MergeChanges(newOrder, MasterViewController.GetStepNumberByName(sessionHistory.Last())); 
} 

public Order MergeChanges(Order newOrder, int step) 
{ 
    PreMerge(newOrder); 

    Order result = null; 
    try 
    { 
     ApplyLookups(ref newOrder); 
     Order oldOrder = (Order)Session["order"]; 

     if (oldOrder == null) 
     { 
      Session["order"] = newOrder; 
      result = newOrder; 
     } 
     else 
     { 
      List<TypeHelper.DecoratedProperty<ModelPageAttribute>> props = null; 
      newOrder.GetType().GetDecoratedProperty<ModelPageAttribute>(ref props); 
      props = props.Where(p => (p.Attributes.Count() > 0 && p.Attributes.First().PageNumber.Contains(step))).ToList(); 
      foreach (var propPair in props) 
      { 
       object oldObj = oldOrder; 
       object newObj = newOrder; 
       if (!string.IsNullOrEmpty(propPair.PropertyPath)) 
       { 
        bool badProp = false; 
        foreach (string propStr in propPair.PropertyPath.Split('\\')) 
        { 
         var prop = oldObj.GetType().GetProperty(propStr); 
         if (prop == null) 
         { 
          badProp = true; 
          break; 
         } 

         oldObj = prop.GetValue(oldObj, BindingFlags.GetProperty, null, null, null); 
         newObj = prop.GetValue(newObj, BindingFlags.GetProperty, null, null, null); 
        } 
        if (badProp) 
          continue; 
       } 

       if (newObj == null) 
        continue; 

       var srcVal = propPair.Property.GetValue(newObj, BindingFlags.GetProperty, null, null, null); 
       var dstVal = propPair.Property.GetValue(oldObj, BindingFlags.GetProperty, null, null, null); 

        var mergeHelperAttr = propPair.Property.GetAttribute<MergeHelperAttribute>(); 
        if (mergeHelperAttr == null) 
        { 
         if (newObj != null) 
          propPair.Property.SetValue(oldObj, srcVal, BindingFlags.SetProperty, null, null, null); 
        } 
        else 
        { 
         var mergeHelper = (IMergeHelper)Activator.CreateInstance(mergeHelperAttr.HelperType); 
         if (mergeHelper == null) 
          continue; 

         mergeHelper.Merge(context, HttpContext.Request, newObj, propPair.Property, srcVal, oldObj, propPair.Property, dstVal); 
        } 
       } 
       result = oldOrder; 
      } 
    } 
    finally 
    { 
    PostMerge(result); 
    } 
    return result; 
} 

Desde mi caso estaba haciendo esto con un asistente, sólo específicos los valores aplicados a cada página para poder solo dar cuenta de las propiedades conocidas en la página actual del asistente, he implementado algunos atributos, una capa (por cierto sobre compleja) ViewController y una capa de validación personalizada. Puedo compartir un poco más de código, pero el código anterior funciona mejor si no estás en una situación tan compleja. Si hay una mejor manera, espero aprenderlo de las respuestas a esta pregunta porque este fue un PITA.

+0

Wow. Agradezco tu respuesta Empecé a pensar en guardar los valores en escondidos en JSON y luego leerlos. ¿Alguna idea? – rebelliard

+0

Dependiendo de los requisitos de su aplicación web que podría funcionar. Sin embargo, se siente realmente sucio y si tienes un gran conjunto de campos, esto podría ser pesado. Sin embargo, probablemente funcionaría. En mi caso, tenía más de 150 campos dinámicos complejos para ser contabilizados, por lo que no era realmente una opción. Espero que encuentres una mejor solución. –

0

Me sorprende que esta pregunta no surja más a menudo, y también me sorprende que la respuesta obvia (en mi humilde opinión) no sea la práctica habitual en estos días: casi todos los POST deben basarse en Ajax.Esto resuelve una gran cantidad de problemas, incluidos

  1. No es necesario volver a llenar los datos del formulario cuando, p. un error de validación o error de aplicación (excepción). Esto es particularmente deseable cuando tiene un estado del lado del cliente (en la verdadera moda de la aplicación web enriquecida).
  2. Sin obligación de realizar la validación del lado del cliente. La validación puede ser del 100% del lado del servidor (donde debe ser de todos modos) y la experiencia del usuario es casi la misma.

Por supuesto, hay algo de trabajo inicial que tiene que hacer para construir un marco para esto, por ejemplo, tengo un conjunto de AjaxUpdate, AjaxNothing, AjaxRedirect, AjaxErrors ... ActionResult tipos que hacen que el que JSON es procesado por un Javascript personalizado. Pero una vez que lo tienes en su lugar, navega sin problemas.

+0

'La validación puede ser 100% del lado del servidor (donde debe ser de todos modos)', tenía la impresión de que MS se inclinaba más * hacia * la validación del lado del cliente presionando el elemento 'DataAnnotation'. De acuerdo, no he pasado el tiempo suficiente en este ámbito para comprender realmente lo que está sucediendo, pero todos los tutoriales que se encuentran en MVC3 y la validación parecen impulsar el enfoque de "Exclusivo Javascript"/DataAnnotation. ¿Me estoy perdiendo de algo? –

+0

@ M.Babcock - Creo que lo que MS ha estado tratando de hacer con DataAnnotations y "Unbtrusive Javascript" es resolver el problema de la validación duplicada del lado del cliente y del lado del servidor con un enfoque diferente: configure las Anotaciones de Datos, y el marco se realice * tanto * la validación del lado del servidor como del lado del cliente de forma transparente, pero en cualquier caso la validación del lado del servidor es autoritativa y la validación del lado del cliente es solo para la experiencia del usuario. Sin embargo, mi solución elimina el problema por completo (un problema que reclamo que está pasado de moda). –

+0

@M.Babcock: ten en cuenta que todavía utilizo DataAnnotations (bueno, he estado prefiriendo FluentValidation últimamente), pero realizo la validación en el servidor y devuelvo los errores con una respuesta AjaxErrors Json que se procesa con Javascript mediante una devolución de llamada de Ajax. –

Cuestiones relacionadas