Sdílet prostřednictvím


Použití slovníku ViewData a implementace tříd ViewModel

od Microsoftu

Stáhnout PDF

Toto je krok 6 bezplatného kurzu aplikace NerdDinner , který vás provede sestavením malé, ale úplné webové aplikace pomocí ASP.NET MVC 1.

Krok 6 ukazuje, jak povolit podporu pro rozsáhlejší scénáře úprav formulářů, a také popisuje dva přístupy, které lze použít k předávání dat z kontrolerů do zobrazení: ViewData a ViewModel.

Pokud používáte ASP.NET MVC 3, doporučujeme postupovat podle kurzů Začínáme S MVC 3 nebo MVC Music Store.

NerdDinner Step 6: ViewData and ViewModel

Probrali jsme řadu scénářů publikování formuláře a probrali jsme, jak implementovat podporu crud (vytvoření, aktualizace a odstranění). Teď naši implementaci DinnersController rozšíříme a povolíme podporu pro rozsáhlejší scénáře úprav formulářů. Při tom probereme dva přístupy, které se dají použít k předávání dat z kontrolerů do zobrazení: ViewData a ViewModel.

Předávání dat z kontrolerů do View-Templates

Jednou z definujících charakteristik modelu MVC je striktní "oddělení zájmů", které pomáhá vynucovat mezi různými komponentami aplikace. Modely, kontrolery a zobrazení mají dobře definované role a povinnosti a vzájemně spolu komunikují dobře definovanými způsoby. To pomáhá podporovat testovatelnost a opakované použití kódu.

Když se třída Controller rozhodne vykreslit odpověď HTML zpět klientovi, zodpovídá za explicitní předání do šablony zobrazení všechna data potřebná k vykreslení odpovědi. Šablony zobrazení by nikdy neměly provádět načítání dat ani logiku aplikace – a místo toho by se měly omezit tak, aby měly pouze vykreslovací kód, který je řízen modelem nebo daty předávaných kontrolerem.

V tuto chvíli jsou data modelu předávaná naší třídou DinnersController do našich šablon zobrazení jednoduchá a přímočará – seznam objektů Dinner v případě Index() a jeden objekt Dinner v případě Details(), Edit(), Create() a Delete(). S tím, jak do naší aplikace přidáváme další možnosti uživatelského rozhraní, budeme často muset předat více než jen tato data, abychom mohli vykreslit odpovědi HTML v rámci našich šablon zobrazení. Například můžeme chtít změnit pole Země v našich zobrazeních Pro úpravy a vytváření z textového pole HTML na rozevírací seznam. Místo toho, abychom v šabloně zobrazení pevně zakódoval rozevírací seznam s názvy zemí a oblastí, můžeme ho chtít vygenerovat ze seznamu podporovaných zemí a oblastí, který dynamicky vyplňujeme. Budeme potřebovat způsob, jak předat objekt Dinner a seznam podporovaných zemí a oblastí z našeho kontroleru do šablon zobrazení.

Podívejme se na dva způsoby, jak toho dosáhnout.

Použití slovníku ViewData

Základní třída Controller zveřejňuje vlastnost slovníku ViewData, kterou lze použít k předání dalších datových položek z Kontrolerů do zobrazení.

Pokud například chceme podporovat scénář, kdy chceme změnit textové pole Country v zobrazení Edit z textového pole HTML na rozevírací seznam, můžeme aktualizovat metodu akce Edit() tak, aby (kromě objektu Dinner) předala objekt SelectList, který lze použít jako model rozevíracího seznamu "Countries".

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    ViewData["Countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

    return View(dinner);
}

Konstruktor výše uvedeného seznamu SelectList přijímá seznam zemí a oblastí, kterými se má rozevírací seznam naplnit, a také aktuálně vybranou hodnotu.

Potom můžeme aktualizovat šablonu zobrazení Edit.aspx tak, aby používala pomocnou metodu Html.DropDownList() místo pomocné metody Html.TextBox(), která byla použita dříve:

<%= Html.DropDownList("Country", ViewData["Countries"] as SelectList) %>

Výše uvedená pomocná metoda Html.DropDownList() má dva parametry. První je název prvku formuláře HTML pro výstup. Druhým je model SelectList, který jsme předali prostřednictvím slovníku ViewData. K přetypování typu ve slovníku jako SelectList používáme klíčové slovo "as" jazyka C#.

Když teď spustíme aplikaci a přejdeme v prohlížeči na adresu URL /Dinners/Edit/1 , uvidíme, že uživatelské rozhraní pro úpravy bylo aktualizováno tak, aby místo textového pole zobrazovalo rozevírací seznam zemí a oblastí:

Snímek obrazovky uživatelského rozhraní pro úpravy se zvýrazněným rozevíracím seznamem zemí a oblastí červenou šipkou

Vzhledem k tomu, že také vykreslíme šablonu zobrazení Pro úpravy metodou HTTP-POST Edit (ve scénářích, kdy dojde k chybám), budeme chtít zajistit, abychom tuto metodu také aktualizovali tak, aby při vykreslování šablony zobrazení ve scénářích chyb přidala do zobrazení ViewData selectList:

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
    
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
    
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        ViewData["countries"] = new SelectList(PhoneValidator.AllCountries, dinner.Country);

        return View(dinner);
    }
}

A náš scénář úprav DinnersController teď podporuje DropDownList.

Použití modelu ViewModel

Přístup ke slovníku ViewData má výhodu v tom, že je poměrně rychlý a snadno implementovatelný. Někteří vývojáři ale nemají rádi používání slovníků založených na řetězcích, protože překlepy můžou vést k chybám, které se v době kompilace nezachytí. Netypový slovník ViewData také vyžaduje použití operátoru "as" nebo přetypování při použití jazyka silného typu, jako je C# v šabloně zobrazení.

Alternativním přístupem, který bychom mohli použít, je ten, který se často označuje jako model ViewModel. Při použití tohoto vzoru vytváříme třídy silného typu, které jsou optimalizované pro naše konkrétní scénáře zobrazení a které zveřejňují vlastnosti pro dynamické hodnoty a obsah, které naše šablony zobrazení potřebují. Naše třídy kontroleru pak mohou tyto třídy optimalizované pro zobrazení naplnit a předat do naší šablony zobrazení k použití. To umožňuje zabezpečení typů, kontrolu kompilace a intellisense editoru v rámci šablon zobrazení.

Pokud například chcete povolit scénáře úprav formuláře večeře, můžeme vytvořit třídu "DinnerFormViewModel", jak je znázorněno níže, která zveřejňuje dvě vlastnosti silného typu: objekt Dinner a model SelectList potřebný k naplnění rozevíracího seznamu Země:

public class DinnerFormViewModel {

    // Properties
    public Dinner     Dinner    { get; private set; }
    public SelectList Countries { get; private set; }

    // Constructor
    public DinnerFormViewModel(Dinner dinner) {
        Dinner = dinner;
        Countries = new SelectList(PhoneValidator.AllCountries, dinner.Country);
    }
}

Pak můžeme aktualizovat naši metodu akce Edit(), aby pomocí objektu DinnerFormViewModel vytvořila objekt Dinner, který načteme z úložiště, a pak ho předáme do naší šablony zobrazení:

//
// GET: /Dinners/Edit/5

[Authorize]
public ActionResult Edit(int id) {

    Dinner dinner = dinnerRepository.GetDinner(id);
    
    return View(new DinnerFormViewModel(dinner));
}

Potom aktualizujeme šablonu zobrazení tak, aby očekávala objekt DinnerFormViewModel místo objektu Dinner, a to tak, že v horní části stránky edit.aspx změníme atribut "inherits":

Inherits="System.Web.Mvc.ViewPage<NerdDinner.Controllers.DinnerFormViewModel>

Jakmile to uděláme, intellisense vlastnosti "Model" v naší šabloně zobrazení se aktualizuje tak, aby odrážela objektový model typu DinnerFormViewModel, který předáváme:

Snímek obrazovky s oknem editoru kódu s rozevíracím seznamem a položkou seznamu Večeře zvýrazněnou modrým obdélníkem

Snímek obrazovky s oknem editoru kódu s rozevíracím seznamem a položkou Seznamu adres zvýrazněnou šedým tečkovaným obdélníkem

Pak můžeme kód zobrazení aktualizovat, aby se s ním pracovalo. Všimněte si níže, že neměníme názvy vstupních elementů, které vytváříme (elementy formuláře budou mít stále názvy "Title", "Country") – ale aktualizujeme pomocné metody HTML pro načtení hodnot pomocí třídy DinnerFormViewModel:

<p>
    <label for="Title">Dinner Title:</label>
    <%= Html.TextBox("Title", Model.Dinner.Title) %>
    <%=Html.ValidationMessage("Title", "*") %>
</p>

<p>
    <label for="Country">Country:</label>
    <%= Html.DropDownList("Country", Model.Countries) %>                
    <%=Html.ValidationMessage("Country", "*") %>
</p>

Také aktualizujeme naši metodu Edit post tak, aby při vykreslování chyb používala třídu DinnerFormViewModel:

//
// POST: /Dinners/Edit/5

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Edit(int id, FormCollection collection) {

    Dinner dinner = dinnerRepository.GetDinner(id);

    try {
        UpdateModel(dinner);

        dinnerRepository.Save();

        return RedirectToAction("Details", new { id=dinner.DinnerID });
    }
    catch {
        ModelState.AddModelErrors(dinner.GetRuleViolations());

        return View(new DinnerFormViewModel(dinner));
    }
}

Můžeme také aktualizovat metody akce Create() tak, aby znovu použily přesně stejnou třídu DinnerFormViewModel a povolily v nich také rozevírací seznam Zemí. Níže je implementace HTTP-GET:

//
// GET: /Dinners/Create

public ActionResult Create() {

    Dinner dinner = new Dinner() {
        EventDate = DateTime.Now.AddDays(7)
    };

    return View(new DinnerFormViewModel(dinner));
}

Níže je uvedena implementace metody HTTP-POST Create:

//
// POST: /Dinners/Create

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Dinner dinner) {

    if (ModelState.IsValid) {

        try {
            dinner.HostedBy = "SomeUser";

            dinnerRepository.Add(dinner);
            dinnerRepository.Save();

            return RedirectToAction("Details", new { id=dinner.DinnerID });
        }
        catch {
            ModelState.AddModelErrors(dinner.GetRuleViolations());
        }
    }

    return View(new DinnerFormViewModel(dinner));
}

Obrazovka Upravit a Vytvořit teď podporují rozevírací seznamy pro výběr země nebo oblasti.

Třídy ViewModel s vlastním tvarem

Ve výše uvedeném scénáři naše třída DinnerFormViewModel přímo zveřejňuje objekt modelu Dinner jako vlastnost spolu s podpůrnou vlastností modelu SelectList. Tento přístup funguje dobře ve scénářích, kdy uživatelské rozhraní HTML, které chceme vytvořit v rámci naší šablony zobrazení, relativně úzce odpovídá objektům doménového modelu.

Ve scénářích, kde tomu tak není, je jednou z možností, kterou můžete použít, vytvoření třídy ViewModel s vlastním tvarem, jejíž objektový model je optimalizovanější pro využití zobrazením – a který může vypadat úplně jinak než základní objekt doménového modelu. Může například potenciálně vystavit různé názvy vlastností nebo agregovat vlastnosti shromážděné z více objektů modelu.

Třídy ViewModel vlastního tvaru lze použít jak k předávání dat z kontrolerů do zobrazení k vykreslení, tak i ke zpracování dat formuláře zasílaných zpět do metody akce kontroleru. V tomto pozdějším scénáři můžete mít metodu akce aktualizovat objekt ViewModel s daty vystavenými ve formuláři a pak použít instanci ViewModel k mapování nebo načtení skutečného objektu doménového modelu.

Třídy ViewModel s vlastním tvarem mohou poskytovat velkou flexibilitu a jsou něco, co je třeba prozkoumat, kdykoli najdete kód vykreslování v šablonách zobrazení nebo kód pro publikování formuláře uvnitř vašich metod akcí, který začíná být příliš komplikovaný. To často značí, že vaše doménové modely přesně neodpovídají uživatelskému rozhraní, které generujete, a že vám může pomoct zprostředkující třída ViewModel s vlastním tvarem.

Další krok

Teď se podíváme na to, jak můžeme použít částečné části a stránky předlohy k opětovnému použití a sdílení uživatelského rozhraní v naší aplikaci.