您好,登錄后才能下訂單哦!
閱讀目錄:
7.HtmlHelper、HtmlHelper<T>中的ViewModel的類型推斷
8.控制ViewModel中的某個屬性的呈現(使用PartialView部分視圖細粒度控制ViewModel的呈現)
9.模板的裝飾者模式(PartialView與ViewModel的嵌套使用(簡))
在View中用來根據當前View中引入的強類型ViewModel生成HTMLDom結構的核心功能都被封裝在以HtmlHelper為首的對象模型中,包括HtmlHelper<T>泛型類型,它直接派生自HtmlHelper基類,這兩個類型的功能都是圍繞著如何生成前端所需要的HTML結構和一些常用的UI元素;
但是這兩個類型所能做的事情很有限,它們只是龐大生成功能的核心模型;我們使用的都是圍繞著這兩個類型的擴展方法,如:
@Html.EditorForModel()
在當前View中引用的Html屬性其實是一個HtmlHelper<T>類型的屬性,定義代碼:
public HtmlHelper<TModel> Html { get; set; }
該類型被定義在public abstract class WebViewPage<TModel> : WebViewPage類中,其實該類是一個模板化代碼生成的基類;我們在ASP.NETMVC項目中添加的所有View文件都會直接或間接的繼承自該類型,在View中引入的類型定義:
@model MvcApplication4.Models.Customer
正是這里泛型類型的類型參數,所以圍繞著HtmlHelper<T>的擴展方法才變成靈活的泛型的代碼生成接口;因為他們彼此通過強大的泛型類型推斷,依次的推斷下去,最終會到達擴展方法的內部,如:
@Html.EditorFor(model => model.Shopping)
這意思是說在View中輸出一個編輯model.Shopping屬性的文本框HtmlDom結構,但是我們調用的明明是一個沒有任何類型形參的方法,其實它已經通過上面說將的環節進行了類型關聯;
畫紅線的部分是View所使用的強類型HtmlHelper<T>對象,類型參數是我們在View中通過@model的方式定義的;畫綠色的部分也是強類型的EditorFor<T>方法,同樣該泛型方法已經被類型推斷過了,看泛型方法的定義:
public static class EditorExtensions { public static MvcHtmlString EditorFor<TModel, TValue>(this HtmlHelper<TModel> html, Expression<Func<TModel, TValue>> expression); }
上述代碼中加粗的部分正是關鍵所在,這里擴展的其實是我們在View中引入的強類型參數的HtmlHelper<MvcApplication4.Models.Customer>,這樣任何圍繞HtmlHelper<T>進行擴展的擴展方法都會最終使用到類型ViewModel;
對于ViewModel的呈現一直都是被系統控制著,雖然一個簡單的字符串類型字段可以用一個文本框的HtmlDom結構方式呈現出來,但是那僅僅是代表著沒有任何業務概念的功能性設置,也就是出發點是從CLR類型系統考慮的,而不是特定領域角度;如果這個字符串代表著某種業務概念,那么我們希望通過更人性化的方式讓用戶使用,而不是一個硬生生的文本框;我們可能會需要提供了一個供自動輸入提示的HtmlDom結構,該結構可能還需要其他的UI成員協助,如:自動提示可能需要JS、后臺Service接口等一系列成員相互協調完成;
這是一個簡單的需求,在大型項目中這樣的功能很常見,也是到處會使用到,不單單是一個兩個頁面,N多頁面都會有一點點的差異性,但是整體功能都會差不多,這樣我們只需要在設計的時候適當的提供一些接口就可以了;
那么ASP.NETMVC是如何生成前臺所需要的HtmlDom結構的呢?前面一章我們總結了,對于ViewModel的呈現形式只會有兩種,一種是Edit一種是Display,不會有其他的呈現形式,所以在圍繞著HtmlHelper對象的擴展方法中大多數都是以這種類別區分的,Edit一組,Display一組;
到目前位置我們已經知道ViewModel與View之間的橋梁是Model元數據,可以簡單的理解為HtmlHelper<T> 一系列擴展方法都是通過獲取Model元數據信息來控制到底需要輸出什么形式的HtmlDom結構,而Model元數據都是通過Model元數據控制特性來完成的,這就可以通過控制Model元數據來控制Model的呈現細節;
public class Address { [UIHint("CustomAddress")] [Display(Name = "地址")] public string AddressId { get; set; } }
我們在Address類型中為AddressId屬性加上一個UIHint類型的特性,其實意思是想說明我們在程序內部使用的是使用地址ID,而在現實的時候我們希望將原來很單調的地址ID編程一個更人性化的地址顯示方式,比如:位于什么省、什么市等等一些其他的地理信息;
在ASP.NETMVC內部有一個internal static class TemplateHelpers 類型的模板輔助類,該類是大部分模板化輸出的幫助接口,在該類的內部定義了一套模板化使用的字典:
視圖的類型:
static readonly Dictionary<DataBoundControlMode, string> modeViewPaths = new Dictionary<DataBoundControlMode, string> { { DataBoundControlMode.ReadOnly, "DisplayTemplates" }, { DataBoundControlMode.Edit, "EditorTemplates" } };
這里定義了兩組類型,也就是顯示、編輯,這兩組類型將作為查找自定義模板的物理文件夾路徑,同樣ModelMedata中的同一個屬性在不同的顯示類型中將有不同的判斷作用;
編輯、顯示:
static readonly Dictionary<string, Func<HtmlHelper, string>> defaultDisplayActions = new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) { { "EmailAddress", DefaultDisplayTemplates.EmailAddressTemplate }, { "HiddenInput", DefaultDisplayTemplates.HiddenInputTemplate }, { "Html", DefaultDisplayTemplates.HtmlTemplate }, { "Text", DefaultDisplayTemplates.StringTemplate }, { "Url", DefaultDisplayTemplates.UrlTemplate }, { "Collection", DefaultDisplayTemplates.CollectionTemplate }, { typeof(bool).Name, DefaultDisplayTemplates.BooleanTemplate }, { typeof(decimal).Name, DefaultDisplayTemplates.DecimalTemplate }, { typeof(string).Name, DefaultDisplayTemplates.StringTemplate }, { typeof(object).Name, DefaultDisplayTemplates.ObjectTemplate }, }; static readonly Dictionary<string, Func<HtmlHelper, string>> defaultEditorActions = new Dictionary<string, Func<HtmlHelper, string>>(StringComparer.OrdinalIgnoreCase) { { "HiddenInput", DefaultEditorTemplates.HiddenInputTemplate }, { "MultilineText", DefaultEditorTemplates.MultilineTextTemplate }, { "Password", DefaultEditorTemplates.PasswordTemplate }, { "Text", DefaultEditorTemplates.StringTemplate }, { "Collection", DefaultEditorTemplates.CollectionTemplate }, { typeof(bool).Name, DefaultEditorTemplates.BooleanTemplate }, { typeof(decimal).Name, DefaultEditorTemplates.DecimalTemplate }, { typeof(string).Name, DefaultEditorTemplates.StringTemplate }, { typeof(object).Name, DefaultEditorTemplates.ObjectTemplate }, };
這是兩組顯示模式的模板化操作方法的字典,可以看出同一個HiddenInput特性將在不同的顯示模式先輸出不同的HtmlDom結構;
在我們的ASP.NETMVC項目中要同樣的有兩組文件夾DisplayTemplates、EditorTemplates,這兩個文件夾將會是系統查找的路徑;
我們在DisplayTemplates目錄下創建了一個用來顯示客戶地址信息的自定義模板,其實也就是PartialView部分視圖,用來重用UI;在該部分視圖中,我們寫點測試數據:
@model string <div> <h3>@Model</h3> <h3>地址:上海市、長寧區</h3> <h3>氣溫:-1~10</h3> <h3>交通:方便出行</h3> </div>
然后我們刷新一下界面,看如何個性化了地址顯示;
這樣我們就可以控制細粒度的ViewModel顯示;
其實我們應該能夠領悟到通過PartialView與HtmlHelper彼此互相嵌套能讓原本單一的部分視圖變成一個強大的具有設計模式功能的模板裝飾者模式;想想看,如果我們將這里的AddressId類型再設計成復雜的類型,然后在該復雜的類型內部我們嵌套了一個原本在其他地方使用的地址類型ViewModel,而且剛好該類型也具有相應的部分是視圖,這樣我們就可以將ViewModel的嵌套使用與PartialView嵌套使用相結合,這樣就可以使用類似設計模式中的裝飾者模式來完成很多UI上的展現重用功能;
作者:王清培
出處:http://wangqingpei557.blog.51cto.com/
本文版權歸作者和51CTO共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接,否則保留追究法律責任的權利。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。