2010-01-14 64 views
12

Estoy tratando de formular una solución para la falta de un "grupo de casilla" en ASP.NET MVC. La forma típica de implementar esto es tener casillas de verificación del mismo nombre, cada una con el valor que representa.ASP.NET MVC Checkbox Grupo

<input type="checkbox" name="n" value=1 /> 
<input type="checkbox" name="n" value=2 /> 
<input type="checkbox" name="n" value=3 /> 

Cuando presentado, se delimitan por comas todos los valores de la posición de solicitud "n" .. así lo soliciten [ "n"] == "1,2,3" si los tres se comprueban cuando se presente. En ASP.NET MVC, puede tener un parámetro de n como matriz para aceptar esta publicación.

public ActionResult ActionName(int[] n) { ... } 

Todo lo anterior funciona bien. El problema que tengo es que cuando la validación falla, las casillas de verificación no se restauran a su estado verificado. Alguna sugerencia.

Código Problema: (I comenzó con la asp.net mvc proyecto por defecto)

controlador

public class HomeController : Controller 
    { 
     public ActionResult Index() 
     { var t = getTestModel("First"); 
      return View(t); 
     } 

     [AcceptVerbs(HttpVerbs.Post)] 
     public ActionResult Index(TestModelView t) 
     { if(String.IsNullOrEmpty(t.TextBoxValue)) 
       ModelState.AddModelError("TextBoxValue", "TextBoxValue required."); 
      var newView = getTestModel("Next"); 
      return View(newView); 
     } 

     private TestModelView getTestModel(string prefix) 
     { var t = new TestModelView(); 
      t.Checkboxes = new List<CheckboxInfo>() 
      { new CheckboxInfo(){Text = prefix + "1", Value="1", IsChecked=false}, 
       new CheckboxInfo(){Text = prefix + "2", Value="2", IsChecked=false} 
      }; 
      return t; 
     } 
    } 
    public class TestModelView 
    { public string TextBoxValue { get; set; } 
     public List<CheckboxInfo> Checkboxes { get; set; } 
    } 
    public class CheckboxInfo 
    { public string Text { get; set; } 
     public string Value { get; set; } 
     public bool IsChecked { get; set; } 
    } 
} 

ASPX

<% 
using(Html.BeginForm()){ 
%> <p><%= Html.ValidationSummary() %></p> 
    <p><%= Html.TextBox("TextBoxValue")%></p> 
    <p><% 
    int i = 0; 
    foreach (var cb in Model.Checkboxes) 
    { %> 
     <input type="checkbox" name="Checkboxes[<%=i%>]" 
      value="<%= Html.Encode(cb.Value) %>" <%=cb.IsChecked ? "checked=\"checked\"" : String.Empty %> 
      /><%= Html.Encode(cb.Text)%><br /> 
    <%  i++; 
    } %></p> 
    <p><input type="submit" value="submit" /></p> 
<% 
} 
%> 

Código de Trabajo

Controlador

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Index(TestModelView t) 
{ 
    if(String.IsNullOrEmpty(t.TextBoxValue)) 
    { ModelState.AddModelError("TextBoxValue", "TextBoxValue required."); 
     return View(t); 
    } 
    var newView = getTestModel("Next"); 
    return View(newView); 
} 

ASPX

int i = 0; 
foreach (var cb in Model.Checkboxes) 
{ %> 
    <input type="checkbox" name="Checkboxes[<%=i%>].IsChecked" <%=cb.IsChecked ? "checked=\"checked\"" : String.Empty %> value="true" /> 
    <input type="hidden" name="Checkboxes[<%=i%>].IsChecked" value="false" /> 
    <input type="hidden" name="Checkboxes[<%=i%>].Value" value="<%= cb.Value %>" /> 
    <input type="hidden" name="Checkboxes[<%=i%>].Text" value="<%= cb.Text %>" /> 
    <%= Html.Encode(cb.Text)%><br /> 
<%  i++; 
} %></p> 
<p><input type="submit" value="submit" /></p> 

Por supuesto, algo similar se podría hacer con HTML ayudantes, pero esto funciona.

+0

Coloque el código de su controlador. –

+0

Como dicen en The Matrix; no hay estado;) http: // stackoverflow.com/questions/1473483/asp-net-mvc-and-viewstate – magnus

+0

@magnus: Eso es mucho por lo que probablemente sea un problema simple en el controlador. –

Respuesta

6

No sé cómo resolver su problema, pero se puede definir sus casillas de verificación con este código: se necesitan

<%= Html.CheckBox("n[0]") %><%= Html.Hidden("n[0]",false) %> 
<%= Html.CheckBox("n[1]") %><%= Html.Hidden("n[1]",false) %> 
<%= Html.CheckBox("n[2]") %><%= Html.Hidden("n[2]",false) %> 

campos ocultos, ya que si la casilla de verificación no está marcada, la forma no envía ningún valor. Con campo oculto envía falso.

Su método post será:

[HttpPost] 
public ActionResult Test(bool[] n) 
{ 
    return View(); 
} 

puede que no sea óptimo y estoy abierto a comentarios, pero funciona :)

EDITAR

versión extendida:

<%= Html.CheckBox("n[0].Checked") %><%= Html.Hidden("n[0].Value",32) %><%= Html.Hidden("n[0].Checked",false) %> 
<%= Html.CheckBox("n[1].Checked") %><%= Html.Hidden("n[1].Value",55) %><%= Html.Hidden("n[1].Checked",false) %> 
<%= Html.CheckBox("n[2].Checked") %><%= Html.Hidden("n[2].Value",76) %><%= Html.Hidden("n[2].Checked",false) %> 

Su método de publicación será:

[HttpPost] 
public ActionResult Test(CheckedValue[] n) 
{ 
    return View(); 
} 

public class CheckedValue 
{ 
    public bool Checked { get; set; } 
    public bool Value { get; set; } 
} 

Lo escribí sin VS, por lo que puede necesitar poca corrección.

+0

Podría hacer algo al respecto, aunque tendría que vincular eso a la lista de valores. Como en los ejemplos de la pregunta, necesito saber que [0] es 1 o en el caso real, 79 o lo que sea. Supongo que podría manejar eso con el almacenamiento de la lista de valores de la sesión. –

+0

¡Bingo! buen pensamiento. Había olvidado un par de cosas ... 1. hacer referencia a .Value y 2. que cuando tienes cualquier parte de una matriz con un valor publicado, la carpeta crea el objeto en la posición. Si omite un índice, no creará ninguno más allá del salto. –

1

Bueno ... las casillas de verificación no van a conocer su estado por sí solas, especialmente si no está utilizando el helper Html.CheckBox (si lo está, vea la respuesta de LuKLed). Deberá poner el estado verificado de cada casilla en su ViewData (o Modelo) y luego realizar una búsqueda en su Vista de una forma u otra.

Advertencia: realmente feo código de prueba de concepto:

controlador:

//validation fails 
ViewData["checkboxn"] = n; 
return View(); 

Vista:

<% int[] n = (int[])ViewData["checkboxn"]; %> 
<input type="checkbox" name="n" value=1 <%= n != null && n.Contains(1) ? "checked=\"checked\"" : "" %> /> 
<input type="checkbox" name="n" value=2 <%= n != null && n.Contains(2) ? "checked=\"checked\"" : "" %> /> 
<input type="checkbox" name="n" value=3 <%= n != null && n.Contains(3) ? "checked=\"checked\"" : "" %> /> 

Todo lo que estoy haciendo aquí está pasando la matriz n volver a la vista, y si contiene un valor para la casilla correspondiente, agregando checked="checked" al elemento.

Probablemente quiera refactorizar esto en un HtmlHelper propio, o hacerlo menos feo, por supuesto.

6

He aquí la solución final:

public static class Helpers 
{ 
    public static string CheckboxGroup<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> propertySelector, int value) where TProperty: IEnumerable<int> 
    { 
     var groupName = GetPropertyName(propertySelector); 
     var modelValues = propertySelector.Compile().Invoke(htmlHelper.ViewData.Model); 

     var svalue = value.ToString(); 
     var builder = new TagBuilder("input"); 
     builder.GenerateId(groupName); 
     builder.Attributes.Add("type", "checkbox"); 
     builder.Attributes.Add("name", groupName); 
     builder.Attributes.Add("value", svalue); 
     var contextValues = HttpContext.Current.Request.Form.GetValues(groupName); 
     if ((contextValues != null && contextValues.Contains(svalue)) || (modelValues != null && modelValues.Contains(value))) 
     { 
      builder.Attributes.Add("checked", null); 
     } 
     return builder.ToString(TagRenderMode.Normal); 
    } 

    private static string GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> propertySelector) 
    { 
     var body = propertySelector.Body.ToString(); 
     var firstIndex = body.IndexOf('.') + 1; 
     return body.Substring(firstIndex); 
    } 
} 

Y en su opinión:

     <%= Html.CheckboxGroup(model => model.DocumentCoverCustom, "1")%>(iv),<br /> 
         <%= Html.CheckboxGroup(model => model.DocumentCoverCustom, "2")%>(vi),<br /> 
         <%= Html.CheckboxGroup(model => model.DocumentCoverCustom, "3")%>(vii),<br /> 
         <%= Html.CheckboxGroup(model => model.DocumentCoverCustom, "4")%>(ix)<br /> 
+0

Sé que esto es muy viejo, pero en lugar de devolver la cadena, debe hacer que el valor devuelto MvcHtmlString sea como el de los otros @Html helpers en mvc. Reemplace 'return builder.ToString (TagRenderMode.Normal);' con 'return new MvcHtmlString (builder.ToString (TagRenderMode.SelfClosing));' esto generará el html y dará etiquetas de cierre automático ya que las entradas son típicamente auto cerradas. ¡Gran solución sin embargo! –

1

Esta solución puede ser de interés para aquellos que quieran un enfoque limpio/simple: Maintain state of a dynamic list of checkboxes in ASP.NET MVC

I realmente no recomendaría el uso de Html.CheckBox a menos que tenga una sola casilla de verificación súper simple unida a un solo bool (o un par de estáticas) a lo sumo). Cuando comienzas a tener listas de casillas de verificación en una sola matriz o casillas de verificación dinámicas, es difícil trabajar con ellas y terminas programando todo el mundo en la mitad del lado del servidor para lidiar con las deficiencias y hacer que todo funcione. Olvídalo, y solo utiliza la solución limpia de HTML enfocada arriba y estarás funcionando rápidamente con menos desorden para mantener en el futuro.

0

Sé que esto debe ser increíblemente tarde, pero sólo en caso de cualquier otra persona se encuentra a sí mismos aquí ..

MVC tiene una forma de manejar grupos de casillas de verificación.

en su modelo de vista:

[Display (Name = "¿Qué se aceptan tarjetas de crédito:")]

public String [] EmployeeRoles {get; conjunto; }

en su página:

  <input id="EmployeeRoles" name="EmployeeRoles" type="checkbox" value="Supervisor" 
      @(Model.EmployeeRoles != null && Model.EmployeeRoles.Contains("Supervisor") ? "checked=true" : string.Empty)/> 
      &nbsp;<span>Supervisor</span><br /> 

      <input id="EmployeeRoles" name="EmployeeRoles" type="checkbox" value="Auditor" 
      @(Model.EmployeeRoles != null && Model.EmployeeRoles.Contains("Auditor") ? "checked=true" : string.Empty)/> 
      &nbsp;<span>Auditor</span><br /> 

      <input id="EmployeeRoles" name="EmployeeRoles" type="checkbox" value="Administrator" 
      @(Model.EmployeeRoles != null && Model.EmployeeRoles.Contains("Administrator") ? "checked=true" : string.Empty) /> 
      &nbsp;<span>Administrator</span> 

me trataron muy duro para crear estos controles con la maquinilla de afeitar, pero no dados. Mantiene creando ese campo oculto al que todos ustedes han hecho referencia. para su casilla de verificación no necesita ese campo oculto, solo el código que he agregado anteriormente. Puede crear un helper html para crear este código para usted.

public static HtmlString CheckboxGroup<TModel, TProperty>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TProperty>> propertySelector, Type EnumType) 
    { 
     var groupName = GetPropertyName(propertySelector); 
     var modelValues = ModelMetadata.FromLambdaExpression(propertySelector, htmlHelper.ViewData).Model;//propertySelector.Compile().Invoke(htmlHelper.ViewData.Model); 

     StringBuilder literal = new StringBuilder(); 

     foreach (var value in Enum.GetValues(EnumType)) 
     { 
      var svalue = value.ToString(); 
      var builder = new TagBuilder("input"); 
      builder.GenerateId(groupName); 
      builder.Attributes.Add("type", "checkbox"); 
      builder.Attributes.Add("name", groupName); 
      builder.Attributes.Add("value", svalue); 
      var contextValues = HttpContext.Current.Request.Form.GetValues(groupName); 
      if ((contextValues != null && contextValues.Contains(svalue)) || (modelValues != null && modelValues.ToString().Contains(svalue))) 
      { 
       builder.Attributes.Add("checked", null); 
      } 

      literal.Append(String.Format("</br>{1}&nbsp;<span>{0}</span>", svalue.Replace('_', ' '),builder.ToString(TagRenderMode.Normal))); 
     } 

     return (HtmlString)htmlHelper.Raw(literal.ToString()); 
    } 

    private static string GetPropertyName<T, TProperty>(Expression<Func<T, TProperty>> propertySelector) 
    { 
     var body = propertySelector.Body.ToString(); 
     var firstIndex = body.IndexOf('.') + 1; 
     return body.Substring(firstIndex); 
    } 

en su página usarlo de esta manera: @ Html.CheckboxGroup (m => m.EmployeeRoles, typeof (Enums.EmployeeRoles))

que utilizan una enumeración, pero se puede utilizar cualquier tipo de colección