Dnes to bude pro změnu krátké, probereme dva způsoby, jak předávat data z controlleru příslušným views, a to pomocí tříd ViewData a ViewModel.
Pomocí těchto tříd můžeme poskytnout bohatší uživatelské rozhraní. Hezké by třeba bylo, kdyby měly stránky pro přidání a upravení večeře místo TextBoxu DropDownList (rozbalovací menu) pro vybrání země. Ale ne že bychom museli do DropDownListu dostupné země natvrdo napsat, musely by se pěkně dynamicky načítat podle toho, pro které státy máme dostupné validace telefonních čísel. Pomocí tříd ViewData a ViewModel nebude těžké toho dosáhnout.
Použití třídy ViewData
Bázová třída Controller, od které je odvozen každý controller, poskytuje vlastnost ViewData, do které můžeme uložit další data, která chceme předat view. Když budeme chtít, aby se seznam zemí zobrazoval jako vysouvací menu, můžeme do ViewData uložit instanci třídy SelectList:
// GET: /Dinners/Edit/2
public ActionResult Edit(int id)
{
Dinner dinner = dinnerRepository.GetDinner(id);
ViewData["Countries"] = new SelectList(PhoneValidator.Countries, dinner.Country);
return View(dinner);
}
Konstruktor SelectListu přijímá v prvním parametru seznam hodnot pro DropDownList a jako druhý parametr přijímá defaultně zvolenou hodnotu. Řádek s TextBoxem pro výběr země v šabloně Edit pak stačí jen přepsat, aby vypadal následovně:
<label for="Country">Země:</label>
<%= Html.DropDownList("Country", ViewData["Countries"] as SelectList) %>
<%=Html.ValidationMessage("Country", "*") %>
I metoda Html.DropDownList přijímá dva parametry – první určuje název ovládacího prvku a druhý parametr je seznam hodnot. Když teď aplikaci spustíme a zadáme URL „/Dinners/Edit/1“ uvidíme to, co je na obrázku:
Protože země chceme vybírat i v případě chyb na stránce s úpravami, musíme patřičně upravit i POST verzi metody Edit:
// POST: /Dinners/Edit/2
[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection formValues)
{
Dinner dinner = dinnerRepository.GetDinner(id);
try
{
UpdateModel(dinner);
dinnerRepository.Save();
return RedirectToAction("Details", new {id = dinner.DinnerID});
}
catch
{
ModelState.AddRuleViolations(dinner.GetRuleViolations());
ViewData["Countries"] = new SelectList(PhoneValidator.Countries, dinner.Country);
return View(dinner);
}
}
Pokud se vám zdá, že jsem zapomněl na metodu Create, protože při vytváření večeří přece taky chceme vybírat zemi, kde se večeře pořádá, máte pravdu. To totiž uděláme pomocí třídy ViewModel, abychom viděli oba způsoby v akci.
Použití třídy ViewModel
Použití ViewData, které jsme si ukázali v předchozí kapitole, má výhodu v tom, že je poměrně rychlé a jednoduché na implementaci. Někdo ale používá tento způsob nerad, protože při psaní názvu klíče může dojít k překlepu, který ale při kompilaci nijak nezjistíme (teď mluvím o té části ve hranatých závorkách).
Alternativním postupem je použití třídy ViewModel. Funguje to tak, že vytvoříme třídu, která má všechny potřebné vlastnosti, a teprve tu předáme danému view. Ukážeme si kód a vzápětí vysvětlím, jak to funguje (pro následující kód vytvořte třídu DinnerFormViewModel v adresáři Controllers):
using System.Web.Mvc;
using NerdDinner.Models;
namespace NerdDinner.Controllers
{
public class DinnerFormViewModel
{
// vlastnosti
public Dinner Dinner { get; private set; }
public SelectList Countries { get; private set; }
// konstruktor
public DinnerFormViewModel(Dinner dinner)
{
Dinner = dinner;
Countries = new SelectList(PhoneValidator.Countries, dinner.Country);
}
}
}
Takže teď k tomu, jak to vlastně bude fungovat. Normálně předáváme view instanci třídy Dinner. Jak ale vidíte, tak konstruktor třídy DinnerFormViewModel přijímá jako parametr také Dinner objekt a pak ho ukládá do vlastnosti, dá se tedy říct, že tato třída obaluje jak Dinner objekt, tak i SelectList naplněný seznamem dostupných zemí. Pak stačí jen mírně upravit akční metodu:
// GET: /Dinners/Edit/2
public ActionResult Edit(int id)
{
Dinner dinner = dinnerRepository.GetDinner(id);
return View(new DinnerFormViewModel(dinner));
}
Ještě musíme provést pár úprav šablony Edit. Nejdříve ji nastavíme tak, aby očekávala DinnerFormViewModel objekt, namísto stávajícího Dinner objektu (podívejte na tag „Page“ v kódu níže). K tomu navíc musíme upravit všechny položky ve formuláři, protože hodnoty jsou od teď dostupné přes Model.Dinner.NazevVlastnosti, nikoliv Model.NazevVlastnosti jako předtím. Kód níže ukazuje všechny změny, včetně upravené definice DropDownListu:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>" %>
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Upravit: <%=Html.Encode(Model.Dinner.Title)%>
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Upravit večeři</h2>
<%=Html.ValidationSummary("Opravte prosím všechny chyby a zkuste to znovu.") %>
<% using (Html.BeginForm()) { %>
<fieldset>
<p>
<label for="Title">Název večeře:</label>
<%=Html.TextBox("Title", Model.Dinner.Title) %>
<%=Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="EventDate">Datum:</label>
<%=Html.TextBox("EventDate", String.Format("{0:g}", Model.Dinner.EventDate))%>
<%=Html.ValidationMessage("EventDate", "*") %>
</p>
<p>
<label for="Description">Popis:</label>
<%=Html.TextArea("Description", Model.Dinner.Description) %>
<%=Html.ValidationMessage("Description", "*")%>
</p>
<p>
<label for="Address">Adresa:</label>
<%=Html.TextBox("Address", Model.Dinner.Address) %>
<%=Html.ValidationMessage("Address", "*") %>
</p>
<p>
<label for="Country">Země:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%=Html.ValidationMessage("Country", "*") %>
</p>
<p>
<label for="ContactPhone">Telefon:</label>
<%=Html.TextBox("ContactPhone", Model.Dinner.ContactPhone) %>
<%=Html.ValidationMessage("ContactPhone", "*") %>
</p>
<p>
<label for="Latitude">Zem. šířka:</label>
<%=Html.TextBox("Latitude", Model.Dinner.Latitude) %>
<%=Html.ValidationMessage("Latitude", "*") %>
</p>
<p>
<label for="Longitude">Zem. délka:</label>
<%=Html.TextBox("Longitude", Model.Dinner.Longitude) %>
<%=Html.ValidationMessage("Longitude", "*") %>
</p>
<p>
<input type="submit" value="Uložit"/>
</p>
</fieldset>
<% } %>
</asp:Content>
Upravenou POST Edit metodu a Create metody nemá vůbec cenu ukazovat. Jediná změna v nich totiž je přepsané „return“ tak, aby vypadalo takto:
return View(new DinnerFormViewModel(dinner));
Bohužel musíme upravit i Create view, aby vše fungovalo jako doposud – změny uvidíte v následujícím zdrojovém kódu:
<%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>" %>
<asp:Content ID="Title" ContentPlaceHolderID="TitleContent" runat="server">
Vytvořit večeři
</asp:Content>
<asp:Content ID="Main" ContentPlaceHolderID="MainContent" runat="server">
<h2>Vytvořit večeři</h2>
<%=Html.ValidationSummary("Opravte prosím všechny chyby a zkuste to znovu.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<p>
<label for="Title">Název:</label>
<%= Html.TextBox("Title", Model.Dinner.Title) %>
<%= Html.ValidationMessage("Title", "*") %>
</p>
<p>
<label for="EventDate">Datum:</label>
<%=Html.TextBox("EventDate", Model.Dinner.EventDate) %>
<%=Html.ValidationMessage("EventDate", "*") %>
</p>
<p>
<label for="Description">Popis:</label>
<%=Html.TextArea("Description", Model.Dinner.Description) %>
<%=Html.ValidationMessage("Description", "*") %>
</p>
<p>
<label for="Address">Adresa:</label>
<%=Html.TextBox("Address", Model.Dinner.Address) %>
<%=Html.ValidationMessage("Address", "*") %>
</p>
<p>
<label for="Country">Země:</label>
<%= Html.DropDownList("Country", Model.Countries) %>
<%=Html.ValidationMessage("Country", "*") %>
</p>
<p>
<label for="ContactPhone">Telefon:</label>
<%=Html.TextBox("ContactPhone", Model.Dinner.ContactPhone) %>
<%=Html.ValidationMessage("ContactPhone", "*") %>
</p>
<p>
<label for="Latitude">Zem. šířka:</label>
<%=Html.TextBox("Latitude", Model.Dinner.Latitude) %>
<%=Html.ValidationMessage("Latitude", "*") %>
</p>
<p>
<label for="Longitude">Zem. délka:</label>
<%=Html.TextBox("Longitude", Model.Dinner.Longitude) %>
<%=Html.ValidationMessage("Longitude", "*") %>
</p>
<p>
<input type="submit" value="Uložit"/>
</p>
</fieldset>
<% }
%>
</asp:Content>
Odměnou za naše snažení nám bude DropDownList i na stránce s vytvářením večeří:
Dnešní kus práce máme za sebou. Všimli jste si ale, jak si jsou view šablony Edit a Create hrozně podobné? Přece by s tím mělo jít něco udělat, a taky že jde. Proto budou naším příštím tématem „partial“ šablony a master pages, které jistě znáte z klasického ASP.NET.