K ASP.NET MVC přistupují někteří vývojáři jako k zázračnému blackboxu, ze kterého vypadávají výsledky tak nějak sami, pokud ovšem nechceme dělat něco neobvyklého a který se někdy chová nevyzpytatelně. Přitom tato technologie není nijak složitá, je dobře promyšlená a umožňuje dělat téměř vše, co chceme. Jednou z prvních oblastí je binding - tedy naplnění vstupních objektů Action metody controlleru a to ze zdrojů hodnot. Zdrojů hodnot je několik (a je možné si napsat další), ale nejdůležitějším a nejpoužívanějším je FormValueProvider - tedy využíti hodnot zaslaného formuláře
Naplnění kolekcí
Nejlépe se jakékoliv plnění popisuje tak, že se ukáže, co je na vstupu a co z toho výchozí nastavení MVC udělá. Prvním předpokladem, aby se název vstupní hodnoty shodoval s názvem objektu či vlastnosti, kterou chceme plnit.
Například zde máme view, které zobrazuje seznam jmen, z nichž se může žádné a nebo všechna vybrat:
@{
IEnumerable<string> names = new[] { "Martin", "Jakub", "Jan" };
}
@using (Html.BeginForm())
{
foreach (var name in names)
{
<input type="checkbox" value="@name" name="names" />@name<br/>
}
<input type="submit" />
}
IEnumerable<string> names = new[] { "Martin", "Jakub", "Jan" };
}
@using (Html.BeginForm())
{
foreach (var name in names)
{
<input type="checkbox" value="@name" name="names" />@name<br/>
}
<input type="submit" />
}
Tato action metoda však bude obsahovat prázdnou kolekci, neboť v požadavku byly z prohlížeče poslány obsahy prvku names a tedy výchozí MVC binder neví, že mají naplnit vstupní parametr authors:
[HttpPost]
public ActionResult Index(List<string> authors)
{
return View();
}
public ActionResult Index(List<string> authors)
{
return View();
}
Stačí však přejmenovat authors na names a vstupní kolekce je naplněna korektně:
[HttpPost]
public ActionResult Index(List<string> names)
{
return View();
}
public ActionResult Index(List<string> names)
{
return View();
}
Přitom nezáleží na velikosti písmen, může se napsat nAmES a i tak se kolekce naplní - zkrátka to není case sensitive.
A jaký má být vstupní typ objektu? Takový, jaký potřebujeme, tedy v případě kolekcí a výše uvedeného případu může vypadat například takto:
- List<string> names
- IEnumerable<string> names
- string[] names
a nebo pokud to mám podat obecně, jakýkoliv typ IEnumerable<T>, ICollection<T>, IList<T>, T[], Collection<T> a List<T>.
Kolekce objektů
jak je vidět, binding do kolekcí s jednoduchým typem funguje bez problému. Co ale v případě, že máme objekty následující třídy (tedy bez nějaké jednoznačné hodnoty):
public class Person
{
public string FirstName { get; set; }
public string LastName {get;set;}
}
{
public string FirstName { get; set; }
public string LastName {get;set;}
}
Checkbox má jen jednu hodnotu a jak tedy přimět binder, aby tuto hodnotu nějak použil na vlastnosti? Můžeme si sice napsat vlastní binder a nějak to udělat, ale takové řešení není elegantní. Jde to ale takto:
@{
IEnumerable<Person> persons = new[] {
new Person{FirstName ="Martin", LastName="Strimpfl"},
new Person{FirstName = "Jakub", LastName="Strimpfl"},
new Person{FirstName = "Jan", LastName = "Strimpfl"}
};
}
@using (Html.BeginForm())
{
foreach (var person in persons)
{
var index = Guid.NewGuid().ToString();
<input type="checkbox" value="@index" name="persons.index" />@person.FirstName@: @person.LastName<br />
<input type="hidden" value="@person.FirstName" name="persons[@index].FirstName" />
<input type="hidden" value="@person.LastName" name="persons[@index].LastName" />
}
<input type="submit" />
}
IEnumerable<Person> persons = new[] {
new Person{FirstName ="Martin", LastName="Strimpfl"},
new Person{FirstName = "Jakub", LastName="Strimpfl"},
new Person{FirstName = "Jan", LastName = "Strimpfl"}
};
}
@using (Html.BeginForm())
{
foreach (var person in persons)
{
var index = Guid.NewGuid().ToString();
<input type="checkbox" value="@index" name="persons.index" />@person.FirstName@: @person.LastName<br />
<input type="hidden" value="@person.FirstName" name="persons[@index].FirstName" />
<input type="hidden" value="@person.LastName" name="persons[@index].LastName" />
}
<input type="submit" />
}
Prvky, které obsahují vlastnosti objektů, budou skryté (hidden) a jejich jméno bude určené podle vzorce:
název kolekce [index prvku].jmeno vlastnosti
Prvek typu checkbox pak bude obsahovat hodnotu indexu - nepředané indexy (=checkbox není aktivní), nebudou zpracovány.
Poznámka: v příkladu používám guid, ale je možné použít konstrukci for a vzestupnou hodnotu - fantazii se meze nekladou.
Tento přístup je i řešením případů, kdy objekt, který používáme, nemá jednoznačný jediný identifikátor, tedy kdy je jedinečnost objektu dána více jak jednou vlastností.
Nejčastěji pro kolekci v modelech pro views používám IEnumerable<T> a nebo obyčejné pole - ale používání polí pro bindování je záludné a tedy jednoznačně upřednostňuji generický typ IEnumerable<T>.
Žádné komentáře:
Okomentovat