在上一篇介紹完了什麼是Model Metadata和Mvc的Html Helper如何利用Metadata來產生開發者想要的Html內容,在這一篇將會介紹框架如何能夠提供一些基礎架設方便產生和我們view能夠對應的Model Metadata。
在這一篇主要會介紹框架本生會定義增加Metadata資訊的部份,然後透過interface的方式,讓產生Metadata的邏輯能夠用實作interface的方式來產生影響。
介 紹完框架的基礎建設之後,將會實作產生當輸入框沒有任何值的時候所顯示的placeholder資訊。也就是這個功能會自動產生WaterMark這個 Metadata資訊,並且透過 EditorTemplate達到沒有值的輸入框出現placeholder的資訊。
另外也會介紹另外一個範例,如何透過Property名稱把UI以TextArea方式呈現。
首先第一步是要建立好修改ModelMetadata的基礎建設。有了這個部分,才來介紹如何實際使用。
首先先從會實際執行內容的interface開始看起。因為每一個application的Convention可能不一樣,因此不管需要什麼Convention的動作,都是實作這個interface來達到。
先來看一下這個interface的定義:
public interface IModelMetadataProcessor
{
void TransformMetadata(System.Web.Mvc.ModelMetadata modelMetadata,
IEnumerable<Attribute> attributes);
}
這個interface很簡單,就是有個方法,這個方法會傳入目前產出的modelMetadata資訊和這個property所有的attribute。然後要做什麼,就看這個interface的實作要對modelMetadata 有沒有需要做任何的處理。
有了interface的定義之後,就來看ExtensibleModelMetadataProvider如何使用那個process的interface。
我們沒有必要整個處理都重寫,因此我們可以繼承DataAnnotationsModelMetadataProvider然後做一些修改。
public class ExtensibleModelMetadataProvider
: DataAnnotationsModelMetadataProvider
{
private readonly IModelMetadataProcessor[] metdataProcessor;
public ExtensibleModelMetadataProvider(
IModelMetadataProcessor[] metadataFilters)
{
metdataProcessor = metadataFilters;
}
protected override System.Web.Mvc.ModelMetadata CreateMetadata(
IEnumerable<Attribute> attributes,
Type containerType,
Func<object> modelAccessor,
Type modelType,
string propertyName)
{
var metadata = base.CreateMetadata(
attributes,
containerType,
modelAccessor,
modelType,
propertyName);
foreach (var item in metdataProcessor)
{
item.TransformMetadata(metadata, attributes);
}
return metadata;
}
}
可以看到,會透過constructor,由DI注入目前有使用的process。然後,先用DataAnnotationsModelMetadataProvider建立出基本的ModelMetadata之後,再把它呼叫到有註冊的process去做處理。
上面基本上框架就定義出來了,欠缺的是要和DI Container說,我們要使用新的ModelMetadataProvider。
首先定義一個Autofac的Module,方便管理這個功能的註冊:
public class ExtensibleModelMetadataModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterType<ExtensibleModelMetadata.ExtensibleModelMetadataProvider>()
.As<ModelMetadataProvider>();
builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
.AsImplementedInterfaces();
}
}
當然也別忘了Global.asax需要註冊這個Module:
Builder.RegisterModule<ExtensibleModelMetadataModule>();
到這邊,整個基礎建設就準備好了,接下來來看如何應用。
這邊的應用會介紹兩個:
這個功能有兩個部分,一個是在產生ModelMetadata的時候,有一個欄位叫做Watermark。這個欄位將會被用來放placeholder的資訊。
另外一個部分是需要寫一個EditorTemplates,這樣Watermark的值才會被用到。
Process的部分
public class PlaceholderProcess : IModelMetadataProcessor
{
public void TransformMetadata(System.Web.Mvc.ModelMetadata modelMetadata,
IEnumerable<Attribute> attributes)
{
if (string.IsNullOrEmpty(modelMetadata.PropertyName) == false &&
string.IsNullOrEmpty(modelMetadata.Watermark))
{
modelMetadata.Watermark = "請輸入" + modelMetadata.DisplayName + "...";
}
}
}
這邊程式碼很簡單,先判斷目前傳進來的欄位有沒有名字,再來就是有沒有被設定過WaterMark。如果_有_欄位並且,_沒有_設定過WaterMark,才用預設的。
這 一這邊的判斷,有判斷Watermark本身是否有值。這個的好處是,假設今天有幾個欄位和其他欄位不一樣,可能會直接用Attribute定義在 Property上面,那麼就以Property的Attribute 為主。這裡的Process,只處理通用型符合Convention邏輯的內容。
View的部分
基本上就是在~Views/Shared/EditorTemplates/string.cshtml增加如下內容:
@model string
@Html.TextBox("", ViewData.TemplateInfo.FormattedModelValue,
new { @class = "form-control",
placeholder = ViewData.ModelMetadata.Watermark })
最後結果
呈現就會變成:
這個部分的功能需要先知道欄位的Convention是什麼,因此我這邊假設所有的TextArea的Property名稱都需要包含TextArea這個字,因此:
public class TextAreaByNameProcess : IModelMetadataProcessor
{
private readonly HashSet<string> textAreaFieldNames =
new HashSet<string>
{
"textarea"
};
public void TransformMetadata(System.Web.Mvc.ModelMetadata modelMetadata,
IEnumerable<Attribute> attributes)
{
if (string.IsNullOrEmpty(modelMetadata.PropertyName) == false &&
string.IsNullOrEmpty(modelMetadata.DataTypeName) &&
textAreaFieldNames.Contains(modelMetadata.PropertyName.ToLower()))
{
modelMetadata.DataTypeName = "MultilineText";
}
}
}
這邊基本上就是在沒有設定任何DataType的情況下,並且property名字符合清單的設定,就把他的DataType設定成為"MultilineText",而Html.EditorFor,看到這個就會用TextArea來做html而不是input text。
通 過開發團隊的Convention和ExtensibleModelMetadataProvider,可以讓UI呈現上面更一致(因為不會因為忘記加 Attribute就導致Placeholder出不來),並且減少ViewModel上面的Attribute的使用。
而使用Process作為實際的處理,讓整個要處理的邏輯變得彈性話,可以依照不同團隊的需求打造不同的處理。
有了ExtensibleModelMetadataProvier簡化一些View的工作之後,我們在下一篇來看一下現在很常用到的內容,也就是用Ajax和Server溝通可能遇到什麼問題的處理。