V předchozím článku jsem ukázal jednu možnost, jak implementovat v ASP.NET MVC (libovolné verze!) „sidebar widgety“. V tomto článku bych rád ukázal další možnosti realizace widgetů, na které nebyl v předchozím článku prostor. Ale ani tento článek není kompletním výčtem všech možností…
SuperModel
V předchozím řešení jsme od ViewModelu, který reprezentuje stránku s widgety, požadovali, aby implementoval rozhraní IViewModelWithWidgets. Pokud ale v našem projektu widgety podporuje drtivá většina stránek, nemusí být toto řešení nejvhodnější. Nechceme totiž v zájmu DRY opakovat definici property Widgets v každém ViewModelu, takže nakonec stejně skončíme u nějakého bázového ViewModelu (který jako jediný tuto property implementuje) a to se nám už nemusí líbit a můžeme začít šilhat po jiném řešení. Přitom ale nemusíme nutně slevovat z našeho požadavku, aby byla celá stránka reprezentována jedním „ViewModelem“.
Data pro celou stránku jsou totiž ve skutečnosti reprezentována třídou ViewDataDictionary, která je v Controlleru i v šabloně dostupná jako property ViewData. Jak již název napovídá, ViewDataDictionary není (zjednodušeně řečeno) nic jiného než Dictionary ze stringu na object, který má ale navíc další properties – a nejvíce na očích je právě property Model, ve které máme typicky instanci našeho ViewModelu.
Vhodnějším řešením, které nás už nebude nabádat k vytváření super-ViewModelu, je tak ukládání dat pro widgety pod nějakým klíčem přímo do ViewData. Kód v akčním filteru (metoda OnActionExecuted) se nám tak ztenčí na pouhé přímé přiřazení do ViewData. I kód v šabloně bude jednodušší – omezí se na zjištění, zda je ve ViewData obsažen požadovaný klíč.
Úplně nám tak odpadne potřeba zavádět nějaká infrastrukturní rozhraní jako v řešení z předchozího článku…
Ajaxizace
Uživatele našeho webu bude asi typicky zajímat především hlavní obsah stránky a nechce být zdržován tím, že bude muset čekat o chvilku déle než se načte nějaký zpropadený widget, který ho třeba v danou chvíli vůbec nezajímá. Jaké máme možnosti, když se vzdáme řešení s použitím Action Filterů?
Od ASP.NET MVC 2 máme k dispozici helper metody Html.Action a Html.RenderAction, pomocí kterých můžeme provést jakýsi Sub-Request. Dojde tedy k zavolání dané akční metody a spuštění vráceného ActionResultu (jako v případě běžného requestu). Výsledek můžeme získat buď ve formě stringu (Html.RenderAction) nebo je výsledek vyrenderován přímo na místo, kde voláme Html.Action. Toto je velmi silná zbraň a díky ní můžeme prezentační logiku koncipovat zcela jinak.
Ideální mi přijde situace, kdy máme pro každý widget jednu akční metodu a k ní odpovídající šablonu (~ascx~Partial View). Mohli bychom mít jeden „velký“ Controller jen pro akční metody widgetů, ale bude lepší mít pro každý widget samostatný Controller – widgety totiž mohou být interaktivní (tj. potřebovat další akční metody pro dodatečnou funkcionalitu) a pravděpodobně budou mít zcela rozdílné závislosti.
Pokud máme pro widget samostaný Controller a samostatnou šablonu, máme ho krásně separovaný. Úkolem hlavní stránky pak bude pouze zajistit volání Html.[Render]Action pro každý widget.
Separace nám přináší i další výhodu – volání akční metody je skvělý kandidát na kešování – můžeme využít standardní atribut OutputCache.
Nyní jsme ale stále v situaci, kdy se celá stránka načítá během jednoho requestu – ajaxizace je ale velmi jednoduchá! Pokud totiž akční metodu neoznačíme atributem ChildActionOnly, můžeme ji volat i samostatně v rámci normálního requestu (samozřejmě musíme zajistit, aby se k ní dalo doroutovat).
V hlavní stránce, která dosud volala Html.[Render]Action, se bude nyní generovat kód, který zajistí, že se widgety dotáhnou pomocí samostatných Ajax requestů (např. s využitím data atributů).
Pokud chceme minimalizovat počet Ajax requestů, můžeme udělat jednu pomocnou akční metodu, která spustí akční metody požadovaných widgetů – tím se dostaneme na jeden request pro hlavní stránku a druhý request pro widgety.
Závěr
V předchozím článku jsem nastínil způsob implementace widgetů, který byl oblíbený v době první verze ASP.NET MVC. V první části toho článku jsem ukázal jeho vylepšení, které nás nenutí používat bázové třídy pro ViewModely a nejen díky tomu je flexibilnější a méně invazivní.
Za nejlepší řešení v současné době ale považuji variantu s Html.[Render]Action, která přináší mnohem větší míru modularity a jako (příjemný) důsledek snadné kešování a podporu pro asynchronní načítání widgetů v samostatném requestu.
Každý projekt si ale žádá svoje a tyto dva články rozhodně nepopisují všechny možnosti, jak je možné widgety v ASP.NET MVC implementovat. Věřím ale, že články mohou posloužit jako inspirace při implementaci widgetů ve vašem projektu…