2011-02-03 12 views
12

Szukam tutaj szybkiego i łatwego sposobu powiązania listy elementów listy checkbox, gdy oddzwonienie występuje w modelu.Czy istnieje sposób powiązania listy wyboru z modelem w asp.net mvc

Wygląda na to, że zwykły sposób zrobienia tego teraz wygląda tak: form.GetValues("checkboxList")[0].Contains("true"); Wydaje się być bolesny i niezupełnie bezpieczny.

Czy istnieje sposób powiązać listę wyboru (które są tworzone z lub bez pomocnika w widoku) lub nawet tablicę danych za co się liczy podczas fazy UpdateModel(myViewModel, form.ToValueProvider()); które mogłyby zapełnić się IList<string> lub string[] wewnątrz z Model ?

Odpowiedz

9

Można zacząć od modelu

public class MyViewModel 
{ 
    public int Id { get; set; } 
    public bool IsChecked { get; set; } 
} 

następnie kontroler:

public class HomeController : Controller 
{ 
    public ActionResult Index() 
    { 
     var model = new[] 
     { 
      new MyViewModel { Id = 1, IsChecked = false }, 
      new MyViewModel { Id = 2, IsChecked = true }, 
      new MyViewModel { Id = 3, IsChecked = false }, 
     }; 
     return View(model); 
    } 

    [HttpPost] 
    public ActionResult Index(IEnumerable<MyViewModel> model) 
    { 
     // TODO: Handle the user selection here 
     ... 
    } 
} 

a View (~/Views/Home/Index.aspx):

<% using (Html.BeginForm()) { %> 
    <%=Html.EditorForModel() %> 
    <input type="submit" value="OK" /> 
<% } %> 

I wreszcie odpowiedni szablon redaktor:

<%@ Control 
    Language="C#" 
    Inherits="System.Web.Mvc.ViewUserControl<AppName.Models.MyViewModel>" %> 
<%= Html.HiddenFor(x => x.Id) %> 
<%= Html.CheckBoxFor(x => x.IsChecked) %> 

Teraz po przesłaniu formularza w akcji POST otrzymasz listę wybranych wartości wraz z ich identyfikatorem.

+0

Pomysł nie jest zły, ale to nie działa w przypadku korzystania z formularza z innych przedmiotów niż ten. Powiedzmy na przykład, że istnieje nazwa użytkownika/hasło + zbiór pól wyboru, w ten sposób nie może on działać:/ – Erick

+2

@Erick, oczywiście, że może działać. Twój model widoku będzie teraz zawierał proste właściwości, takie jak 'Username' i' Password' oraz właściwość kolekcji 'IEnumerable ', która będzie zawierała informacje o polach wyboru. Teraz zamiast używać 'Html.EditorForModel' w twojej formie miałbyś proste' <% = Html.TextBoxFor (x => x.Username)%> 'i' <% = Html.PasswordFor (x => x.Password) %> 'i' <% = Html.EditorFor (x => x.MyCheckboxes)%> ', które będą renderować ten sam szablon edytora. –

+0

Czytałem wiele sposobów na osiągnięcie tego, a to było zdecydowanie najbardziej eleganckie rozwiązanie. Dzięki! – JimDaniel

9

Oto szybki i łatwy sposób. Wystarczy ustawić atrybut value w swoich elementach wejściowych checkbox i nadać im taką samą nazwę: name. Gdybym realizował to w miejscu, chciałbym stworzyć metodę CheckBox pomocnika, który zaczyna name, value i isChecked parametry, ale tutaj jest widok z tylko html potrzebne:

<% using (Html.BeginForm()) { %> 
    <p><input type="checkbox" name="checkboxList" value="Value A" /> Value A</p> 
    <p><input type="checkbox" name="checkboxList" value="Value B" /> Value B</p> 
    <p><input type="checkbox" name="checkboxList" value="Value C" /> Value C</p> 
    <p><input type="checkbox" name="checkboxList" value="Value D" /> Value D</p> 
    <p><input type="checkbox" name="checkboxList" value="Value E" /> Value E</p> 
    <p><input type="checkbox" name="checkboxList" value="Value F" /> Value F</p> 
    <input type="submit" value="OK" /> 
<% } %> 

w kontroler:

[AcceptVerbs(HttpVerbs.Post)] 
public ActionResult Index(IEnumerable<string> checkboxList) 
{ 
    if (checkboxList != null) 
    { 
     ViewData["Message"] = "You selected " + checkboxList.Aggregate("", (a, b) => a + " " + b); 
    } 
    else 
    { 
     ViewData["Message"] = "You didn't select anything."; 
    } 

    return View(); 
} 

Jeśli chcesz, możesz ustawić go jako IList<string>, który będzie zawierał tylko wartości zaznaczonych elementów. Jeśli żadne z pól nie zostanie zaznaczone, będzie to null.

+0

Myślę, że nie jest dobrym pomysłem umieszczenie natywnego kodu HTML tam, ponieważ będzie to ból, jeśli użytkownik chce powiązać model z polami wyboru – tugberk

+0

@tugberk, powiedziałem, że użyłbym metody pomocnika w kodzie produkcyjnym. Próbowałem tylko pokazać odpowiedź na oryginalne pytanie. Dodam kod metody pomocnika. – CoderDennis

+2

To jest poprawna odpowiedź. Jeśli spróbujesz użyć wbudowanych pomocników 'Html.CheckBox()', ** nalegają ** na wstawianie ukrytych pól z 'value =" false "', i to koliduje z wiązaniem modelu. – Yuck

0

Działa to całkiem dobrze za pomocą spinacza modelu niestandardowych i regularne HTML ...

Po pierwsze, formularz HTML w składni Razor:

@using (Html.BeginForm("Action", "Controller", FormMethod.Post)) { 
    <ol> 
     <li><input type="textbox" name="tBox" value="example of another form element" /></li> 

     <li><input type="checkbox" name="cBox" value="1" /> One</li> 
     <li><input type="checkbox" name="cBox" value="2" /> Two</li> 
     <li><input type="checkbox" name="cBox" value="3" /> Three</li> 
     <li><input type="checkbox" name="cBox" value="4" /> Four</li> 
     <li><input type="checkbox" name="cBox" value="5" /> Five</li> 

     <li><input type="submit" /></li> 
    </ol> 
} 

(FormMethod.Post może również być .Get, nie ma znaczenia do tego)

Następnie, w sensie właściwym MVC posiada obiekt modelu, który reprezentuje swój złożenie formularza:

public class CheckboxListExampleModel { 
    public string TextboxValue { get; set; } 
    public List<int> CheckboxValues { get; set; } 
} 

I niestandardowa klasa wiążącego modelu (lubię umieszczać ją w modelu, który jest związany, więc powtórzę model utworzony powyżej, aby pokazać, gdzie powinienem go dodać.Umieszczenie go wewnątrz pozwala także spoiwo używać prywatnych ustawiające własności, co jest dobrą rzeczą):

public class CheckboxListExampleModel { 
    public string TextboxValue { get; private set; } 
    public List<int> CheckboxValues { get; private set; } 

    public class Binder : DefaultModelBinder { 
     public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext) { 
      var model = new CheckboxListExampleModel(); 

      model.TextboxValue = bindingContext.GetValueAsString("tBox"); 

      string checkboxCsv = bindingContext.GetValueAsString("cBox"); 
      // checkboxCsv will be a comma-separated list of the 'value' attributes 
      // of all the checkboxes with name "cBox" which were checked 
      model.CheckboxValues = checkboxCsv.SplitCsv<int>(); 

      return model; 
     } 
    } 
} 

.GetValueAsString() jest metoda rozszerzenie używane dla jasności, to jest tutaj:

public static string GetValueAsString(this ModelBindingContext context, string formValueName, bool treatWhitespaceAsNull = true) { 
     var providerResult = context.ValueProvider.GetValue(formValueName); 
     if (providerResult.IsNotNull() && !providerResult.AttemptedValue.IsNull()) { 
      if (treatWhitespaceAsNull && providerResult.AttemptedValue.IsNullOrWhiteSpace()) { 
       return null; 
      } else { 
       return providerResult.AttemptedValue.Trim(); 
      } 
     } 
     return null; 
    } 

.SplitCsv<T>() jest także metoda rozszerzenia, ale jest to dość powszechna potrzeba i niechlujny kod, który zostawię jako ćwiczenie dla czytelnika.

I wreszcie, twoje działanie w obsłudze formularz złożyć:

[HttpPost] 
public ActionResult Action([ModelBinder(typeof(CheckboxListExampleModel.Binder))] CheckboxListExampleModel model) { 
    // stuff 
}