ASP.NET MVC學習筆記(十一)-超好用的Templates
記得在一兩個月前我就寫過一篇初學ASP.NET MVC學習筆記(九)-Templates
但那時只是看看文章,順便紀錄一點學習心得而已,還沒體會到templates的用處
但最近因為專案做後台時大量的用到templates,才真正的覺得這真是個好東西
所以就來再一次的紀錄簡單的templates的運用
先建個簡單的table及dbml
然後建立一個 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,首先要知道檔案要放哪
有兩種資料夾 DisplayTemplates 和 EditorTemplates
這兩個資料夾的名稱是不能改的。放置的位置跟Controller找view的方式很像
先找同名Controller資料夾下有沒有上面兩種templates資料夾,找不到的話再去Shared資料夾裡面找。
通常因為templates都是共用的,所以我習慣放在Shared裡面
建立完後,就可以開始在裡面寫要以甚麼樣的方式呈現這個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>
附帶一提一個不相關的問題,如果屬性取中文名字,在Html.ValidationMessageFor()裡沒有另外加一個
id給他的話,就會出現下面的錯誤。
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>
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應該也只要寫一次,然後重複利用才是。