Alan Tsai 的學習筆記


學而不思則罔,思而不學則殆,不思不學則“網貸” 為現任微軟最有價值專家 (MVP)、微軟認證講師 (MCT) 、Blogger、Youtuber:記錄軟體開發的點點滴滴 著重於微軟技術、C#、ASP .NET、Azure、DevOps、Docker、AI、Chatbot、Data Science

[iThome 第七屆鐵人賽 27] View相關的處理 - 框架自動增加Model Metadata

在上一篇介紹完了什麽是Model Metadata和Mvc的Html Helper如何利用Metadata來產生開發者想要的Html內容,在這一篇將會介紹框架如何能夠提供一些基礎架設方便產生和我們view能夠對應的Model Metadata。

功能描述

在這一篇主要會介紹框架本生會定義增加Metadata資訊的部份,然後透過interface的方式,讓產生Metadata的邏輯能夠用實作interface的方式來產生影響。

介紹完框架的基礎建設之後,將會實作產生當輸入框沒有任何值的時候所顯示的placeholder資訊。也就是這個功能會自動產生WaterMark這個Metadata資訊,並且透過 EditorTemplate達到沒有值的輸入框出現placeholder的資訊。

另外也會介紹另外一個範例,如何透過Property名稱把UI以TextArea方式呈現。

ExtensibleModelMetadataProvider功能建設

首先第一步是要建立好修改ModelMetadata的基礎建設。有了這個部分,才來介紹如何實際使用。

IModelMetadataProcessor interface的定義

首先先從會實際執行內容的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 有沒有需要做任何的處理。


ExtensibleModelMetadataProvider主題建設


有了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去做處理。


Autofac的註冊


上面基本上框架就定義出來了,欠缺的是要和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>();


到這邊,整個基礎建設就準備好了,接下來來看如何應用。


應用


這邊的應用會介紹兩個:



  1. 欄位自動建立placeholder
  2. 自動產生TextArea而不是input text


欄位自動建立placeholder


這個功能有兩個部分,一個是在產生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 })

最後結果


呈現就會變成:


image



自動產生TextArea而不是input text


這個部分的功能需要先知道欄位的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溝通可能遇到什麼問題的處理。


如果文章對您有幫助,就請我喝杯飲料吧
街口支付QR Code
街口支付QR Code
台灣 Pay QR Code
台灣 Pay QR Code
Line Pay 一卡通 QR Code
Line Pay 一卡通 QR Code
街口支付QR Code
支付寶QR Code
街口支付QR Code
微信支付QR Code
comments powered by Disqus