Lekce 6 - Obsluha formulářů v ASP.NET Core MVC
V předchozím kvízu, Kvíz - MVC, pohled, middleware, routování v ASP.NET Core MVC, jsme si ověřili nabyté zkušenosti z předchozích lekcí.
V dnešním ASP.NET Core tutoriálu si vyzkoušíme techniku
napojení modelu přímo na View. Této technice se říká
model binding a hodí se zejména při práci s formuláři.
Programovat budeme jednoduchou kalkulačku.
Založíme si nový projekt podle šablony ASP.NET Core Web App
(Model-View-Controller), který pojmenujeme MVCKalkulacka:

Touto šablonou nám budou vygenerovány jednotlivé složky
pro MVC komponenty a nastaveny výchozí routy a
konfigurace, které jsme minule dělali ručně. Dále nám
bude vygenerována i složka wwwroot/ pro statický (neměnný)
obsah webu. Zde budeme umisťovat například obrázky, CSS soubory nebo JavaScript
soubory. Visual Studio nám sem do složky
lib/ také automaticky přidalo populární knihovny Bootstrap a jQuery. Minule jsme tuto šablonu
nevyužili, abychom lépe pochopili, jak ASP.NET Core a architektura MVC
funguje.
Jako název projektu nepoužívejte jen
Kalkulacka, jelikož by kolidoval s názvem naší třídy, kterou
si dále vytvoříme.
Bude nám vygenerován i ukázkový projekt. Můžeme si jej zkusit spustit:

My tento projekt nebudeme potřebovat, a proto vyprázdníme veškerý obsah
složek Models/, Controllers/ a Views/ v
okně Solution Explorer. Ponecháme si ovšem soubor
Views/_ViewImports.cshtml, jinak by nám správně nefungovaly tzv.
tag helpers (viz dále).
Pokud bychom začínali s prázdným projektem jako minule, museli bychom tento soubor přidat ručně.
Rovnou si ukažme, jak bude naše hotová kalkulačka vypadat:

Model
Začněme opět modelem, kterým bude třída Kalkulacka. Tu si
vytvoříme ve složce Models/.
Vlastnosti
Modelu přidáme několik veřejných vlastností, konkrétně dvě
vstupní čísla, vybranou operaci a
výsledek. Poslední vlastností bude seznam
typu SelectListItem, který bude obsahovat možné operace pro
pohled. Ten z nich následně vyrenderuje HTML element
<select>. Seznam rovnou naplníme v konstruktoru:
public class Kalkulacka { public int PrvniCislo { get; set; } public int DruheCislo { get; set; } public double Vysledek { get; private set; } public string Operace { get; set; } public List<SelectListItem> MozneOperace { get; private set; } public Kalkulacka() { Operace = "+"; MozneOperace = [ new SelectListItem { Text = "Sečti", Value = "+", Selected = true }, new SelectListItem { Text = "Odečti", Value = "-" }, new SelectListItem { Text = "Vynásob", Value = "*" }, new SelectListItem { Text = "Vyděl", Value = "/" } ]; } }
Vlastnost Text třídy SelectListItem je popisek
možnosti, který vidí uživatel. Value je hodnota, která se
odesílá na server (neměla by obsahovat diakritiku). Můžeme nastavit i
vlastnost Selected, která označuje zda má být položka při
zobrazení stránky vybraná.
Nezapomeneme na přidání
using Microsoft.AspNetCore.Mvc.Rendering pro typ
SelectListItem.
Metoda VypocitejVysledek()
Zbývá jen metoda s nějakou logikou, která podle zvolené
Operace a hodnot v PrvniCislo a
DruheCislo vypočítá Vysledek:
public void VypocitejVysledek() { Vysledek = Operace switch { "+" => PrvniCislo + DruheCislo, "-" => PrvniCislo - DruheCislo, "*" => PrvniCislo * DruheCislo, "/" => PrvniCislo / DruheCislo, _ => 0 }; }
Výsledek se po zavolání metody uloží do vlastnosti
Vysledek. Stejně tak bychom ho mohli i vrátit, jako jsme to
dělali v minulém projektu s náhodným číslem. Pro naše další záměry s
bindingem to ale bude takto výhodnější.
Namísto klasického switch zde využíváme jeho
úspornější
variantu, kterou máme k dispozici od C# 9 a která se zrovna v této
situaci hodí, protože nám značně zjednoduší kód.
Model máme hotový, přidejme si kontroler.
Controller
Kontroler budeme mít v naší aplikaci zase jen jeden. Určitě si vzpomínáme, že kontroler slouží k propojení modelu (logiky) a pohledu (HTML šablony).
Do složky Controllers/ si přidáme nový MVC Controller -
Empty a pojmenujeme ho HomeController. Tento kontroler se
spustí při vstupu na výchozí stránku aplikace, jelikož je na něj v
souboru Program.cs nasměrována výchozí URL adresa. Přejděme
do jeho kódu a akci Index() upravme do následující podoby:
public IActionResult Index() { Kalkulacka kalkulacka = new Kalkulacka(); return View(kalkulacka); }
Při vstupu na stránku se zavolá akce Index(), to již víme.
Tehdy vytvoříme novou instanci modelu, což je stále stejné, jako minule.
Nově však model předáme pohledu jako parametr.
Nezapomeneme na přidání
using MVCKalkulacka.Models pro Kalkulacka.
View
Pro akci Index() vygenerujeme pohled. To uděláme opět
kliknutím pravým tlačítkem kamkoli do akce, zvolením Add View... a
poté Razor View (ne Razor View - Empty). Jako
Template zvolíme Create a Model class nastavíme na
Kalkulacka. Nezapomeneme odškrtnout možnost Use
a layout page, abychom měli v pohledu zahrnutu i základní HTML
strukturu:

Template nám umožňuje do pohledu rovnou předgenerovat nějaký kód, této technice se říká scaffolding. Template Create vygeneruje pohled napojený na zvolený model a k vlastnostem tohoto modelu vygeneruje formulář pro vytvoření instance modelu. Když aplikaci nyní spustíme, vypadá takto:

Vidíme, že Visual Studio vygenerovalo celkem čtyři vstupy. Dva pro
čísla a po jednom pro výsledek a
operaci. Operaci však budeme chtít zadávat pomocí elementu
<select> a výsledek nebudeme vypisovat do formulářového
pole, ale do HTML odstavce <p>.
Pohled Index
Přesuneme se proto do souboru Index.cshtml a změníme ho do
následující podoby:
@model MVCKalkulacka.Models.Kalkulacka <!DOCTYPE html> <html lang="cs"> <head> <meta name="viewport" content="width=device-width" /> <title>Kalkulačka</title> <link rel="stylesheet" href="~/lib/bootstrap/dist/css/bootstrap.min.css" /> </head> <body class="m-3"> <h1>Kalkulačka</h1> <div class="row"> <div class="col-md-4"> <form asp-action="Index"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="PrvniCislo"></label> <input asp-for="PrvniCislo" class="form-control" /> <span asp-validation-for="PrvniCislo" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="DruheCislo"></label> <input asp-for="DruheCislo" class="form-control" /> <span asp-validation-for="DruheCislo" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Operace"></label> @Html.DropDownListFor(model => model.Operace, new SelectList(Model.MozneOperace, "Value", "Text"), new { @class = "form-select" }) <span asp-validation-for="Operace" class="text-danger"></span> </div> <div class="form-group mt-3"> <input type="submit" value="Vypočítej" class="btn btn-primary" /> </div> <p class="h3 mt-3">@Model.Vysledek</p> </form> </div> </div> <script src="~/lib/jquery/dist/jquery.min.js"></script> @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); } </body> </html>
Změn jsme oproti původní podobě šablony až tak moc neprovedli. Na
úplném začátku šablony vidíme nastavení typu modelu
@model MVCKalkulacka.Models.Kalkulacka, na který je pohled
nabindovaný (navázaný). Dále jsme v hlavičce stránky
<head> nastavili titulek stránky a
nalinkovali knihovnu Bootstrap s
předpřipravenými CSS třídami, které využíváme ke stylování celé
stránky.
Všimněme si použití tildy (~) pro získání
cesty do složky wwwroot/, kde se složka s knihovnou Bootstrap nalézá.
Obsah stránky jsme obalili do elementu <body>, na jehož
začátek jsme umístili nadpis. Následuje formulář, který vygenerovalo
Visual Studio a který jsme pouze upravili. Na konci formuláře vypisujeme
vlastnost Vysledek modelu Kalkulacka do HTML odstavce
<p>, čímž ho zobrazíme uživateli.
Tag helpers
Jednotlivá editační pole pro vlastnosti modelu vkládáme tímto stylem:
<div class="form-group"> <label asp-for="NazevVlastnosti"></label> <input asp-for="NazevVlastnosti" class="form-control" /> <span asp-validation-for="NazevVlastnosti" class="text-danger"></span> </div>
Atributy asp-for jsou tzv. tag helpers, pomocí
kterých dokáže ASP.NET Core vygenerovat pro naši vlastnost vhodný
ovládací prvek. Například pro datum se vloží
DatePicker a podobně. Tento atribut zároveň zajišťuje
propojení popisku <label> s odpovídajícím vstupem
<input>.
Chybové hlášky
Atribut asp-validation-for vloží prostor pro výpis
chybové hlášky v případě, že uživatel pole špatně
vyplní. To se zjistí z datového typu vlastnosti nebo případně pomocí
speciálních validačních atributů (o těch si ještě povíme dále v
kurzu). Vše tedy funguje zcela automaticky. Drobnou nevýhodou je, že danou
vlastnost předáváme helperu jako text. Visual Studio nám naštěstí
správnost kódu dokáže zkontrolovat i tak.
Atribut asp-validation-summary na elementu
<div> umístěném na začátku formuláře zajistí
zobrazení shrnutí všech chyb, které při zpracování
formuláře nastanou:
<div asp-validation-summary="ModelOnly" class="text-danger"></div>
Tento atribut může nabývat jedné ze tří hodnot:
All– vypíší se všechny chyby,ModelOnly– vypíší se všechny chyby kromě těch způsobených špatným vyplněním pole,None– nevypíší se žádné chyby.
My používáme hodnotu ModelOnly, která je vhodná v kombinaci
s atributem asp-validation-for. Chyby způsobené špatným
vyplněním pole se zobrazí přímo u daného pole. Zbylé chyby se pak
zobrazí na začátku formuláře.
V rámci tag helpers máme k dispozici i některé speciální HTML elementy.
HTML helper
Můžeme vidět, že kombinujeme tag helpers se starším systémem
vkládání ovládacích prvků pomocí tzv. HTML helperu typu
IHtmlHelper. Ne všechny ovládací prvky jsou totiž v
současnosti tag helpery podporovány, někdy se tomuto řešení proto
nevyhneme. K instanci tohoto helperu přistupujeme přes vlastnost
Html (@Html).
Tímto způsobem například přidáváme pomocí metody
DropDownListFor() element <select> pro výběr
operace. Této metodě předáváme tři argumenty:
- název vlastnosti, do které se má uložit výsledek výběru,
- seznam položek výběru typu
SelectLista - anonymní objekt s atributy a jejich hodnotami, které se mají přidat vygenerovanému elementu.
Seznam SelectList vytváříme z položek uložených v našem
modelu ve vlastnosti MozneOperace. V konstruktoru seznamu taktéž
musíme definovat, jaká vlastnost položky obsahuje hodnotu a jaká popisek
dané položky.
Preferujeme napojování formulářových prvků na vlastnosti
modelu pomocí tag helperů (třeba asp-for) než pomocí
zavináčů. Přeci chceme, aby HTML šablona vypadala co nejvíce jako HTML
kód 
Akce formuláře
Vraťme se ještě na chvíli k samotnému formuláři, kterému nastavujeme
atribut asp-action. Opět se jedná o tag helper z ASP.NET Core,
kterým říkáme, na jakou akci se má obsah formuláře
odeslat při jeho potvrzení (např. kliknutím na tlačítko typu
submit). My chceme, aby se odesílal na akci Index() z
kontroleru HomeController, proto tomuto atributu nastavujeme název
dané akce Index().
V jakém kontroleru se má akce hledat, pak můžeme určit atributem
asp-controller. Bez uvedení tohoto atributu se akce hledá v
kontroleru daného pohledu. V našem případě tento atribut tedy uvádět
nemusíme.
Skripty pro validaci
Vždy je nutné ověřovat, jestli data, která nám uživatel posílá, jsou
v pořádku a odpovídají tomu, co očekáváme. Je tedy nutné data
validovat. Proto na úplném konci elementu
<body> připojujeme skripty potřebné pro správné
fungování automatické validace vstupů ještě ve webovém
prohlížeči.
Tyto skripty se nacházejí v souboru
_ValidationScriptsPartial.cshtml, který nám byl vygenerovaný ve
složce Views/Shared/. Jedná se o tzv. částečný
pohled (partial view), tedy pohled obsahující pouze malou,
znovupoužitelnou část HTML kódu, kterou můžeme vkládat do jiných
pohledů. Používáme k tomu asynchronní
metodu RenderPartialAsync() HTML helperu, které akorát
předáváme název požadovaného pohledu.
Pro správné fungování vyžadují validační skripty ještě i skripty
knihovny jQuery, které máme v projektu
již předinstalované a které načítáme o řádek výše pomocí elementu
<script>.
Pohled _ViewImports
Aby nám tag helpers v projektu fungovaly, je zapotřebí v něm mít i
soubor pojmenovaný _ViewImports.cshtml s následujícím
obsahem:
@using MVCKalkulacka @using MVCKalkulacka.Models @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
Stejně jako v C# souborech, i v .cshtml souborech pracujeme se
jmennými prostory, které obsahují námi používané třídy. Soubor
_ViewImports.cshtml obsahuje příkazy using pro
importování jmenných prostorů, které se mají připojit ke
každému pohledu.
Soubor _ViewImports.cshtml již v projektu máme ve
složce Views/.
Spuštění aplikace
Po spuštění aplikace uvidíme takovýto formulář:

Po jeho odeslání se zatím nic nestane. Pokračovat budeme zase až příště.
V příští lekci, Zpracování dat a validace v ASP.NET Core MVC, se naučíme zpracovávat data odeslaná formulářem. Zmíníme se o fungování HTTP a nakonec zavedeme validační anotace v modelech.
Měl jsi s čímkoli problém? Zdrojový kód vzorové aplikace je ke stažení každých pár lekcí. Zatím pokračuj dál, a pak si svou aplikaci porovnej se vzorem a snadno oprav.

