Pregunta

Ahora he visto algunas preguntas como esta, pero no es exactamente lo que quiero hacer, así que por todos esos gritos duplicados, me disculpo :).

Apenas he tocado ASP.NET MVC pero, por lo que tengo entendido, no hay ViewState/ControlState...bien.Entonces mi pregunta es ¿cuál es la alternativa a retener el estado de un control?¿Volvemos al ASP de la vieja escuela, donde podríamos simular lo que hace ASP.NET ViewState/ControlState creando entradas de formulario ocultas con el estado del control, o con MVC, simplemente asumimos AJAX siempre y retenemos todo el estado del lado del cliente y hacemos AJAX? llama para actualizar?

Esta pregunta tiene algunas respuestas, ¿Mantener el estado de visualización en Asp.net mvc?, pero no exactamente lo que busco en una respuesta.

ACTUALIZAR:Gracias por todas las respuestas hasta el momento.Sólo para aclarar lo que no estoy buscando y lo que estoy buscando:

No busco:

  • solución de sesión
  • Solución de cookies
  • No busco imitar WebForms en MVC

Lo que estoy/estaba buscando:

  • Un método que solo conserva el estado en la devolución de datos si los datos no se reenvían a un control.Piense en WebForms con el escenario de vincular solo una cuadrícula en la carga de la página inicial, es decir.solo volver a vincular los datos cuando sea necesario.Como mencioné, no estoy tratando de imitar WebForms, solo me pregunto qué mecanismos ofrece MVC.
¿Fue útil?

Solución

La convención ya está disponible sin pasar por demasiados obstáculos.El truco consiste en conectar los valores del cuadro de texto según el modelo que pasa a la vista.

[AcceptVerbs(HttpVerbs.Get)]   
public ActionResult CreatePost()
{
  return View();
}

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePost(FormCollection formCollection)
{
  try
  {
    // do your logic here

    // maybe u want to stop and return the form
    return View(formCollection);
  }
  catch 
  {
    // this will pass the collection back to the ViewEngine
    return View(formCollection);
  }
}

Lo que sucede a continuación es que ViewEngine toma formCollection y hace coincidir las claves dentro de la colección con los nombres/valores de ID que tiene en su vista, utilizando los ayudantes HTML.Por ejemplo:

<div id="content">

  <% using (Html.BeginForm()) { %>

  Enter the Post Title: <%= Html.TextBox("Title", Model["Title"], 50) %><br />
  Enter the Post Body: <%= Html.TextArea("Body", Model["Body"]) %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

¿Observa que el cuadro de texto y el área de texto tienen los ID de Título y Cuerpo?Ahora, ¿observa cómo estoy configurando los valores del objeto Modelo de vista?Dado que pasó una FormCollection (y debe configurar la vista para que esté fuertemente tipada con una FormCollection), ahora puede acceder a ella.O, sin escribir fuertemente, simplemente puede usar ViewData["Title"] (creo).

MARICÓN Tu ViewState mágico.Este concepto se llama convención sobre configuración.

Ahora, el código anterior está en su forma más simple y sin formato usando FormCollection.Las cosas se ponen interesantes cuando empiezas a usar ViewModels, en lugar de FormCollection.Puede comenzar a agregar su propia validación de sus Modelos/ViewModels y hacer que el controlador muestre los errores de validación personalizados automáticamente.Aunque esa es una respuesta para otro día.

Sugeriría usar un PostFormViewModel en lugar del objeto Post, pero para cada uno.De cualquier manera, al requerir un objeto en el método de acción, ahora obtienes un método IsValid() al que puedes llamar.

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult CreatePost(Post post)
{

  // errors should already be in the collection here
  if (false == ModelState.IsValid())
    return View(post);

  try
  {
    // do your logic here

    // maybe u want to stop and return the form
    return View(post);
  }
  catch 
  {
    // this will pass the collection back to the ViewEngine
    return View(post);
  }
}

Y su vista fuertemente tipada debería modificarse:

<div id="content">

  <% using (Html.BeginForm()) { %>

  Enter the Post Title: <%= Html.TextBox("Title", Model.Title, 50) %><br />
  Enter the Post Body: <%= Html.TextArea("Body", Model.Body) %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

Puede ir un paso más allá y mostrar los errores también en la vista, directamente desde el ModelState que configuró en el controlador.

<div id="content">

  <%= Html.ValidationSummary() %>

  <% using (Html.BeginForm()) { %>

  Enter the Post Title: 
    <%= Html.TextBox("Title", Model.Title, 50) %>
    <%= Html.ValidationMessage("Title") %><br />

  Enter the Post Body: 
    <%= Html.TextArea("Body", Model.Body) %>
    <%= Html.ValidationMessage("Body") %><br />

  <%= Html.SubmitButton() %>

  <% } %>

</div>

Lo interesante de este enfoque es que notará que no estoy configurando el resumen de validación ni los mensajes de validación individuales en la Vista.Me gusta practicar los conceptos de DDD, lo que significa que mis mensajes de validación (y resúmenes) se controlan en mi dominio y se pasan en forma de colección.Luego, recorro la colección (si existe algún error) y los agrego a la colección actual ModelState.AddErrors.El resto es automático cuando regresa Ver (publicar).

Se han descartado muchas convenciones.Algunos libros que recomiendo encarecidamente y que cubren estos patrones con mucho más detalle son:

Y en ese orden, el primero cubre los aspectos prácticos de todo el marco MVC.Este último cubre técnicas avanzadas fuera del ámbito oficial de Microsoft, con varias herramientas externas para hacerte la vida mucho más fácil (Castle Windsor, Moq, etc).

Otros consejos

Se supone que la vista es tonta en el patrón MVC, solo que muestra lo que el controlador le da (obviamente, a menudo terminamos con algo de lógica allí, pero la premisa es que no lo sea), como resultado, los controles no son responsables de Su estado, vendrá del controlador cada vez.

No puedo recomendar el libro Pro ASP.NET MVC de Steven Sanderson por APRESS suficiente para enfrentar este patrón y esta implementación de él.

En formularios web, los valores de control se mantienen en ViewState para que usted (teóricamente) no necesita reinicializar y tal con cada devolución de devolución. Los valores son (nuevamente teóricamente) mantenidos por el marco.

En ASP.NET MVC, si sigue el paradigma, no necesita mantener el estado en los elementos de formulario. Los valores del elemento de formulario están disponibles en la publicación donde su controlador puede actuar en consecuencia (validación, actualizaciones de la base de datos, etc.). Para cualquier elemento de formulario que se muestre una vez que se procesa la publicación, usted (el desarrollador) es responsable de inicializarlos: el marco no lo hace automáticamente por usted.

Dicho esto, he leído sobre un mecanismo llamado TempData que permite que su controlador pase datos a otro controlador después de una redirección. En realidad, es una variable de sesión (o cookie si la configura como tal) pero se limpia automáticamente después de la próxima solicitud.

La respuesta realmente depende de los tipos de controles para los que está tratando de mantener el estado. Para los controles HTML básicos, entonces es muy fácil mantener el estado con sus modelos, para hacer esto necesita crear una vista fuertemente escrita.

Entonces, si tuvimos un modelo de usuario con las propiedades: nombre de usuario, nombre completo, correo electrónico, podemos hacer lo siguiente en la vista:

<%= Html.ValidationSummary() %>

<% using (Html.BeginForm()) { %>
  <fieldset>
    <legend>User details</legend>
    <%= Html.AntiForgeryToken() %>

    <p>
      <label for="Username">Username:</label>
      <%= Html.Textbox("Username", Model.Username, "*") %>
    </p>
    <p>
      <label for="FullName">FullName:</label>
      <%= Html.Textbox("FullName", Model.FullName, "*") %>
    </p>
    <p>
      <label for="Email">Email:</label>
      <%= Html.Textbox("Email", Model.Email, "*") %>
    </p>
    <p>
       <input type+"submit" value="Save user" />
    </p>
  </fieldset>
<% } %>

Luego tendríamos dos acciones de controlador que muestran esta vista, una para Get y otra para POST:

[AcceptVerbs(HttpVerbs.Get)]
public ActionResult User()
{
  return View(new User())
}

[AcceptVerbs(HttpVerbs.Post)]
[ValidateAntiForgeryToken]
public ActionResult User([Bind(Include = "Username,FullName,Email")]User user)
{
   if (!ModelState.IsValid()) return View(user);

   try
   {
     user.save()
     // return the view again or redirect the user to another page
   }
   catch(Exception e)
   {
     ViewData["Message"] = e.Message;
     return View(user)
   }
}

¿Es esto lo que estás buscando? ¿O desea mantener el estado de los modelos que no se muestran en un formulario entre solicitudes?

Lo clave para recordar es que su código se ejecuta en el servidor durante la duración de la solicitud y finaliza, la única información que puede aprobar entre sus solicitudes son los datos básicos de formulario HTML, los parámetros de URL y la información de la sesión.

Como otras personas han mencionado, recomiendo encarecidamente el marco MVC Pro ASP.NET de Steve Sandersan para una comprensión completa de trabajar con el marco MVC.

  • campos ocultos, como:

    <% using (Html.BeginForm<SomeController>(c=>c.SomeAction(null))) {%>
      <%= Html.Hidden("SomeField", Model.SomeField)%>
      <%= Html.Hidden("AnotherField", Model.AnotherField)%>
    
  • configurar el modelo específico y no tener ningún campo explícito (le brinda campos ocultos).En el siguiente ejemplo, el controlador completa el modelo con los valores recibidos de la última publicación, por lo que esto habilita una opción sin js en la página que puede filtrar según un estado:

    Some Filter: <% using( Html.BeginForm<SomeController>(
            c => c.SomeAction(model.SomeField, model.AnotherField, model.YetAnotherField, null, model.SomeOtherField)
            )) { %>
                <%= Html.DropDownList("status", Model.StatusSelectList)%>
                <input type="submit" value="Filter" class="button" />
                <% } %>
    
  • use métodos de extensión para crear campos, si solo desea que los campos se completen con valores publicados cuando muestra mensajes de validación fallidos en el formulario enviado
  • en asp.net mvc 2 introdujeron una forma de guardar una instancia en un campo oculto...codificado + (creo) firmado
  • TempData si todo lo anterior no funciona (pasa por la sesión y se limpia en la siguiente solicitud)
  • Como mencionaste, cuando usas ajax, el estado ya está en los campos cargados previamente en el sitio del cliente.Si necesita hacer una publicación completa, actualice cualquier campo que necesite con su js.

Las anteriores son todas opciones independientes diferentes para lograrlo que se pueden utilizar en diferentes escenarios.Hay más opciones que no mencioné, es decir.cookies, sesión, almacenar cosas en la base de datos (como para un asistente de varios pasos reanudable), parámetros pasados ​​a una acción.No existe un mecanismo único para gobernarlos a todos, y no debería haberlo.

Creo que la mejor manera de hacer esto es serializar su modelo original a un campo oculto, luego deserializarlo y actualizar el modelo en la publicación. Esto es algo similar al enfoque ViewState, solo usted tiene que implementarlo usted mismo. Yo uso esto:

Primero necesito algunos métodos que faciliten las cosas:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using LuvDaSun.Extensions;
using System.Web.UI;

namespace LuvDaSun.Web.Mvc
{
    public static class HtmlHelperExtensions
    {
        static LosFormatter _losFormatter = new LosFormatter();
        public static string Serialize(this HtmlHelper helper, object objectInstance)
        {
            var sb = new StringBuilder();
            using (var writer = new System.IO.StringWriter(sb))
            {
                _losFormatter.Serialize(writer, objectInstance);
            }
            return sb.ToString();
        }


    }

    [AttributeUsage(AttributeTargets.Parameter)]
    public class DeserializeAttribute : CustomModelBinderAttribute
    {
        public override IModelBinder GetBinder()
        {
            return new DeserializeModelBinder();
        }
    }

    public class DeserializeModelBinder : IModelBinder
    {
        static LosFormatter _losFormatter = new LosFormatter();

        public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
        {
            if (bindingContext.ModelType.IsArray)
            {
                var type = bindingContext.ModelType.GetElementType();
                var serializedObjects = (string[])bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string[]));
                var deserializedObjects = Array.CreateInstance(bindingContext.ModelType.GetElementType(), serializedObjects.Length);

                for (var index = 0; index < serializedObjects.Length; index++)
                {
                    var serializedObject = serializedObjects[index];
                    var deserializedObject = _losFormatter.Deserialize(serializedObject);

                    deserializedObjects.SetValue(deserializedObject, index);
                }

                return deserializedObjects;
            }
            else
            {
                var serializedObject = (string)bindingContext.ValueProvider.GetValue(bindingContext.ModelName).ConvertTo(typeof(string));
                var deserializedObject = _losFormatter.Deserialize(serializedObject);

                return deserializedObject;
            }
        }
    }

}

Luego, en mi controlador, tengo algo como esto (para actualizar un producto)

    public ActionResult Update(string productKey)
    {
        var model = _shopping.RetrieveProduct(productKey);

        return View(model);
    }
    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Update([Deserialize]Shopping.IProduct _model, FormCollection collection)
    {
        UpdateModel(model);

        model.Save();

        return RedirectAfterPost();
    }

Y necesito un campo oculto que contenga el objeto serializado en la forma:

    <% 
        using (Html.BeginRouteForm("Product", FormMethod.Post, new { id = UniqueID, }))
        {
    %>
<%= Html.Hidden("Model", Html.Serialize(Model)) %>
    <h1>
        Product bewerken</h1>
    <p>
        <label for="<%=UniqueID %>_Name">
            Naam:</label>
        <input id="<%=UniqueID %>_Name" name="Name" type="text" value="<%= Html.AttributeEncode(Model.Name) %>"
            class="required" />
        <br />
    </p>
    <p>
        Omschrijving:<br />
        <textarea id="<%= UniqueID %>_Description" name="Description" cols="40" rows="8"><%= Html.Encode(Model.Description) %></textarea>
        <br />
    </p>
    <p>
        <label for="<%=UniqueID %>_Price">
            Prijs:</label>
        <input id="<%= UniqueID %>_Price" name="Price" type="text" value="<%= Model.Price.ToString("0.00") %>"
            class="required" />
        <br />
    </p>
    <ul class="Commands">
        <li><a href="" class="ClosePopup">Annuleren</a></li>
        <li>
            <input type="submit" value="Opslaan" /></li>
    </ul>
    <% 
        } 
    %>

    <script type="text/javascript">

        jQuery('#<%= UniqueID %>').validate();

    </script>

Como puede ver, se agrega un campo oculto (modelo) al formulario. Contiene la información de serialización para el objeto original. Cuando se publica el formulario, el campo oculto también se publica (por supuesto) y los contenidos están deserializados por el ModelBinder personalizado al objeto original que luego es actualizado y guardado por el controlador.

Tenga en cuenta que el objeto que está serializando debe decorarse con el atributo serializable o debe tener un typeconverter que pueda convertir el objeto en una cadena.

ViewState utiliza laSFORMATRATER (ViewState en Forms WebForms. También ofrece cifrado de los datos de serialización.

saluda ...

Ajax llama lo que hacemos. Si estás hablando de cuadrículas en general, consulte Jqgrid y cómo recomiendan la implementación de AJAX.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top