ASP.NET MVC學習筆記(十一)-超好用的Templates

ASP.NET MVC學習筆記(十一)-超好用的Templates

記得在一兩個月前我就寫過一篇初學ASP.NET MVC學習筆記(九)-Templates

但那時只是看看文章,順便紀錄一點學習心得而已,還沒體會到templates的用處

但最近因為專案做後台時大量的用到templates,才真正的覺得這真是個好東西

所以就來再一次的紀錄簡單的templates的運用

 

先建個簡單的table及dbml

image

然後建立一個 TemplateTB的 partial class,設定一點簡單的attribute

[MetadataTypeAttribute(typeof(TemplateTBPartial))]
public partial class TemplateTB
{
    private class TemplateTBPartial
    { 
        public Guid ID { get; set; }

        [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
        [DisplayName("選擇日期")]
        [Required(ErrorMessage = "請選擇日期")]
        public DateTime 日期 { get; set; }

        [DisplayName("年齡")]
        [Required(ErrorMessage = "請選擇年齡")]
        public string 類別 { get; set; }

        [DisplayName("性別")]
        [Required(ErrorMessage = "請選擇性別")]
        public bool 性別 { get; set; }
    }
    partial void OnCreated()
    {
        _ID = Guid.NewGuid();
    }
}

首先先來解釋一下templates使用時的幾種方式

第一種:ASP.NET MVC內已有內建幾種預設的templates,例如 bool,EmailAddress,Url 等等等....

所以如果跟屬性型別一致,並在View上是用呼叫templates的Helper的話,就會自動以預設格式顯示

(View上怎麼以templates的方式叫用等等再說),也可以自己覆寫預設的格式。

第二種:C#裡面沒有Url這種型別,那要怎麼叫用預設的Url templates呢?這時只要在屬性上面加上

一個  [DataType(DataType.Url)] 的attribute,他就知道你要叫用Url的templates

第三種:如果預設的templates不夠我用,我希望自訂templates,並在partial class就指定這個屬性要用

哪個templates,該怎麼設定呢,也是在該屬性上加一句  [UIHint("自訂的templates名稱")] 就行了。

 

所以來改寫一下上面的partial class,我希望日期用Date這個templates,年齡用自訂的年齡選項templages

性別用預設的bool templates

[DataType(DataType.Date)]
[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}")]
[DisplayName("選擇日期")]
[Required(ErrorMessage = "請選擇日期")]
public DateTime 日期 { get; set; }

[UIHint("年齡選項")]
[DisplayName("年齡")]
[Required(ErrorMessage = "請選擇年齡")]
public string 類別 { get; set; }

[DisplayName("性別")]
[Required(ErrorMessage = "請選擇性別")]
public bool 性別 { get; set; }

改完之後,開始實作自己的templates,首先要知道檔案要放哪

有兩種資料夾 DisplayTemplatesEditorTemplates

這兩個資料夾的名稱是不能改的。放置的位置跟Controller找view的方式很像

先找同名Controller資料夾下有沒有上面兩種templates資料夾,找不到的話再去Shared資料夾裡面找。

通常因為templates都是共用的,所以我習慣放在Shared裡面

image

image

建立完後,就可以開始在裡面寫要以甚麼樣的方式呈現這個templates囉。

首先先知道兩個常用的東西

ViewData.TemplateInfo.HtmlFieldPrefix  -- 代表使用這個templates的屬性名稱

ViewData.TemplateInfo.FormattedModelValue -- 屬性的值

有屬性名稱,有值,剩下UI的部分就可以靈活運用啦。

Shared\EditorTemplates\年齡選項.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%ViewData[ViewData.TemplateInfo.HtmlFieldPrefix] = ViewData.TemplateInfo.FormattedModelValue;%>
<%=Html.DropDownList("", DropDownListMenu.年齡選項(), "請選擇年齡")%>

第二行是利用MVC會自動把值帶入同欄位名稱的特性

所以先寫ViewData[欄位名稱]=值,之後如果下方有同樣欄位名稱的表單,就會自動把值帶入

第三行我希望這個templates可以用下拉選單的方式來呈現,所以我用了Html.DropDownList這個Helper

第一個參數是表單欄位名稱,給空值就好,因為它會自動加上去

第二個參數是傳入一個 IEnumerable<SelectListItem>,所以另外寫了一個class,以靜態的方法呼叫

第三個參數是預設的選項

接著來看這個靜態方法

public static class DropDownListMenu
{
    public static IEnumerable<SelectListItem> 年齡選項()
    {
        List<SelectListItem> items = new List<SelectListItem>();
        items.Add(new SelectListItem { Text = "0~20歲", Value = "0~20歲"});
        items.Add(new SelectListItem { Text = "21~40歲", Value = "41~60歲" });
        items.Add(new SelectListItem { Text = "61~80歲", Value = "61~80歲" });
        items.Add(new SelectListItem { Text = "80歲以上", Value = "80歲以上" });
        return items;
    }

 	public static string Show年齡選項(string s)
    {
      return 年齡選項()
			.Where(p=>p.Value.Equals(s,StringComparison.CurrentCultureIgnoreCase))
			.FirstOrDefault().Text;
    }
}

然後寫另外一個日期屬性的templates,因為我將他的DataType設為Data,因此我的templates取名為Date就行了

Shared\EditorTemplates\Date.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<script type="text/javascript">
    $(function () {
        $(".datepicker").datepicker({ "dateFormat": "yy-mm-dd" })
    });
</script>
<%=Html.TextBox("", String.Format("{0:yyyy-MM-dd}"
, ViewData.TemplateInfo.FormattedModelValue), new { @class = "datepicker" })%>

日期的部分我想用jQuery的datepicker的方式呈現,所以呢把該匯入的js檔跟css檔匯入

2-6行其實可以寫在masterpage裡面,但我為了清楚起見先寫在這裡

再來就是用Html.TextBox,幫他加一個class就好囉。

上面的兩個templates都是放在EditorTemplates裡面的,現在再建兩個templates放在DisplayTemplates裡

由於DisplayTemplates比較單純,我就直接貼Code了。

Shared\DisplayTemplates\Date.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%=Html.Encode(String.Format("{0:yyyy-MM-dd}",ViewData.TemplateInfo.FormattedModelValue)) %>

Shared\DisplayTemplates\年齡選項.ascx

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<%=Html.Encode(DropDownListMenu.Show年齡選項(ViewData.TemplateInfo.FormattedModelValue.ToString()))%>

這樣Code的部分都寫完了,雖然寫了很多,但仔細想想這些東西都是可以重複利用十幾二十次,而且很好維護

的話,還是很划得來,剩下就是在View上怎麼去叫用了。

<%=Html.Display() %>                 --叫用DisplayTemplates裡面的templates
<%=Html.DisplayFor() %>             --以強型別的方式叫用DisplayTemplates裡面的templates
<%=Html.DisplayForModel() %>    --顯示整個Model

<%=Html.Editor() %>                   --叫用EditoerTemplates裡面的templates
<%=Html.EditorFor() %>               --以強型別的方式叫用EditoerTemplates裡面的templates
<%=Html.EditorForModel() %>      --編輯整個Model

各有三種寫法,但也可以說只有兩種,因為Display()跟DisplayFor()只是一種用強型別,一種不是而已。

DisplayForModel()是叫用整個class,如果想更改格式必須在資料夾底下建立同名的Partial View(例如

這個例子,名字就要取作 TemplateTB.ascx),這個我比較不常用到,所以還不是很熟悉。

那就直接來看看View的Code跟實際的畫面吧

Create.aspx跟Edit.aspx長的一樣

<div class="editor-label">
    <%= Html.LabelFor(model => model.日期) %>
</div>
<div class="editor-field">
    <%= Html.EditorFor(model => model.日期)%>
    <%= Html.ValidationMessageFor(model => model.日期,"", new { id = "Msg_日期" })%>
</div>
<div class="editor-label">
    <%= Html.LabelFor(model => model.類別) %>
</div>
<div class="editor-field">
    <%= Html.EditorFor(model => model.類別, new { id = "Msg_類別" })%>
    <%= Html.ValidationMessageFor(model => model.類別, "", new { id = "Msg_類別" })%>
</div>
<div class="editor-label">
    <%= Html.LabelFor(model => model.性別)%>
</div>
<div class="editor-field">
    <%= Html.EditorFor(model => model.性別) %>
    <%= Html.ValidationMessageFor(model => model.性別, "", new { id = "Msg_性別" })%>
</div>

 

image

image image

 

 

附帶一提一個不相關的問題,如果屬性取中文名字,在Html.ValidationMessageFor()裡沒有另外加一個

id給他的話,就會出現下面的錯誤。

image

Details.aspx

<div class="display-label">日期</div>
<div class="display-field"><%= Html.DisplayFor(p=>p.日期) %></div>

<div class="display-label">類別</div>
<div class="display-field"><%= Html.DisplayFor(p=>p.類別) %></div>

<div class="display-label">性別</div>
<div class="display-field"><%= Html.DisplayFor(p => p.性別)%></div>

image

 

Html.DisplayFor()跟Html.EditorFor()共有六種呼叫方法

其中有一個參數 string templateName,是可以在特殊情況下,你希望這個屬性用其他的templates的話,

可以使用這個參數傳入templates名稱,如

<%= Html.EditorFor(model => model.類別,"指定其他的Templates")%>

這樣一來,這個屬性就不會去用原先設定的 "年齡選項"的templates,改用"指定其他的Templates"這個名稱

的templates來顯示。

 

另外一個參數 object additionalViewData,是可以傳入其他資訊,如

<%= Html.EditorFor(model => model.類別, new { 附加條件="Test" })%>

這樣的話,在templates內就可以用 ViewData["附加條件"] , 抓到 Test這個值。

 

還有一個參數是 string htmlFieldName,是可以改變表單欄位名稱的,例如

<%= Html.EditorFor(model => model.類別,"指定其他的Templates","xxx")%>

這樣在"指定其他的Templates" 這個templates中,用ViewData.TemplateInfo.HtmlFieldPrefix抓出來的

值就會變成 xxx

 

 

 

呼,寫了好多。整篇文章可能有點雜亂,因為除了templates之外,還加入了我自己實作的部分

但我只是想強調templates有多好用,像是日期,性別這種簡單的東西之外,還可以把連動式的縣市選單

,或是上傳檔案的按鈕做成tempaltes的話,以後要用時只要在partial class上加個屬性就搞定了。

 

Tony Stark說好的武器只要發射一次,所以好的Code應該也只要寫一次,然後重複利用才是。