緊接着上面的內容,我們繼續看下動態模型頁面交互實現方式,內容如下:
1,如何實現動態表單
2,如何接收表單數據並綁定到動態模型上
一、如何實現動態表單
由於模型信息都是后台自定義配置的,並不是固定不變的結構,所以沒有辦法直接在頁面上寫出對應的表單數據,而需要通過解析模型的結構,動態的生成對應的表單。在說具體實現方法前,我們先來看下我們想要達到的效果。
Html.Raw(FormGenerator.Generate(Model,Properties))
FormGenerator.Generate包含兩個參數,一個動態模型對象,一個需要呈現的屬性列表,方法返回最終生成的form表單html,然后通過Html.Raw呈現到頁面上。
下面介紹一下實現過程,首先定義一個IDynamicFormGenerator接口,代碼如下:
public interface IDynamicFormGenerator
{
string Generate(object obj, IEnumerable<RuntimeModelMeta.ModelPropertyMeta> properties);
}
接口中只包含一個方法,就是我們上面實例代碼用到的方法。接口實現邏輯上很簡單,只需要循環每一個屬性,根據屬性的特點生成一個表單,並把對象數據綁定到表單上,比如屬性如果是bool類型,我們可以生成一個checkbox,然后根據obj對應的屬性值是true還是false,進而設置checkbox的選中狀態。具體實現代碼:
public class DynamicFormGenerator : IDynamicFormGenerator
{
public string Generate(object obj, IEnumerable<RuntimeModelMeta.ModelPropertyMeta> properties)
{
StringBuilder builder = new StringBuilder();
//循環屬性集合
foreach (var item in properties)
{
//TODO:根據屬性信息生成表單並綁定數據
string fieldhtml="";
//把生成的html加入到stringbulder中
builder.Append(fieldhtml);
}
//返回最終的html結果
return builder.ToString();
}
}
這個方法里最主要的部分就是如何根據屬性的類型生成對應的表單,就是如何得到上面的fieldhtml?我們再來看下前面定義的RuntimeModelMeta
public class RuntimeModelMeta
{
public int ModelId { get; set; }
public string ModelName { get; set; }//模型名稱
public string ClassName { get; set; }//類名稱
public string Properties{get;set;}//屬性集合json序列化結果
public class ModelPropertyMeta
{
public string Name { get; set; }//對應的中文名稱
public string PropertyName { get; set; } //類屬性名稱
public int Length { get; set; }//數據長度,主要用於string類型
public bool IsRequired { get; set; }//是否必須輸入,用於數據驗證
public string ValueType { get; set; }//數據類型,可以是字符串,日期,bool等
}
}
ModelPropertyMeta里包含了一個ValueType信息,就是當前屬性的數據類型,那我們是否可以根據這個來確定生成的表單形式?答案是否定的,因為即使是同一種類型也會呈現不同的表單,比如都是字符串,可能有的要求呈現下拉框,有的要求呈現復選框,所以只依靠ValueType還不夠,我們可以給RuntimeModelMeta增加一個屬性,專門用於設置表單形式的,改造后代碼如下:
public class RuntimeModelMeta
{
public int ModelId { get; set; }
public string ModelName { get; set; }//模型名稱
public string ClassName { get; set; }//類名稱
public string Properties{get;set;}//屬性集合json序列化結果
public class ModelPropertyMeta
{
public string Name { get; set; }//對應的中文名稱
public string PropertyName { get; set; } //類屬性名稱
public int Length { get; set; }//數據長度,主要用於string類型
public bool IsRequired { get; set; }//是否必須輸入,用於數據驗證
public string ValueType { get; set; }//數據類型,可以是字符串,日期,bool等
public string ShowType { get; set; }//表單形式
}
}
有了ShowType,我們就可以根據設置的類型來生成對應的表單。首先先定義個表單生成器接口,代碼如下:
public interface IDynamicFormFieldGenerator
{
//根據傳遞的屬性及對象,生成表單
string Generate(object obj, RuntimeModelMeta.ModelPropertyMeta meta,bool onlyform=false);
//這個表示當前的實現是針對哪一種ShowType的
string ForType { get; }
}
然后針對每一種ShowType實現一個生成器,比如針對checkbox類型的ShowType,我們實現一個生成器,代碼如下:
public class CheckboxFieldGenerator : IDynamicFormFieldGenerator
{
public string ForType
{
get
{
return "checkbox";
}
}
public string Generate(object obj, RuntimeModelMeta.ModelPropertyMeta meta,bool onlyform=false)
{
//把動態對象轉換成一個DynamicEntity,為的是后面獲取數據方便,因為DynamicEntity支持通過索引獲取屬性數據
DynamicEntity entity = obj as DynamicEntity;
if (obj == null)
{
throw new NullReferenceException("DynamicEntity");
}
//通過entity[meta.PropertyName]獲取到屬性數據
return string.Format("<input id='{1}' name='{1}' type='checkbox' value='{0}'/>",
entity[meta.PropertyName]?.ToString(), meta.PropertyName);
}
}
其他ShowType類型,可以根據自己系統的需要,直接實現即可,這里不再一一列舉了。
有了表單構造器,我們再回頭完善下DynamicFormGenerator,在DynamicFormGenerator中,我們需要根據屬性的ShowType信息獲取到IDynamicFormFieldGenerator,我們可以定義一個DynamicFormFieldGeneratorProvider,方便我們得到我們所需要的IDynamicFormFieldGenerator,具體實現代碼:
//提供者接口定義
public interface IDynamicFormFieldGeneratorProvider
{
IDynamicFormFieldGenerator Get(string type);
}
public class DynamicFormFieldGeneratorProvider: IDynamicFormFieldGeneratorProvider
{
private readonly IHttpContextAccessor _httpContextAccessor;
private IEnumerable<IDynamicFormFieldGenerator> _generators;
public DynamicFormFieldGeneratorProvider(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
public IDynamicFormFieldGenerator Get(string type)
{
//通過依賴注入,獲取到構造器實現集合,所以我們需要把所有的構造器實現都注冊到ServiceCollection中
if (_generators==null)
{
_generators= _httpContextAccessor.HttpContext.RequestServices.GetServices<IDynamicFormFieldGenerator>();
}
if (_generators==null)
{
throw new NotSupportedException("IDynamicFormFieldGenerator");
}
//根據type找到第一個符合條件的構造器,並返回
IDynamicFormFieldGenerator g = _generators.FirstOrDefault(m => m.ForType == type);
if (g==null)
{
throw new NotSupportedException("not supproted for " + type + "'s form field generator");
}
return g;
}
}
條件都准備好了,直接完善DynamicFormGenerator,最終代碼如下:
public class DynamicFormGenerator : IDynamicFormGenerator
{
private readonly IDynamicFormFieldGeneratorProvider _fieldGeneratorProvider;
public DynamicFormGenerator(IDynamicFormFieldGeneratorProvider provider)
{
_fieldGeneratorProvider = provider;
}
public string Generate(object obj, IEnumerable<RuntimeModelMeta.ModelPropertyMeta> properties)
{
StringBuilder builder = new StringBuilder();
foreach (var item in properties)
{
//根據showtype獲取表單構造器
IDynamicFormFieldGenerator fieldGenerator = _fieldGeneratorProvider.Get(item.ShowType);
builder.Append(fieldGenerator.Generate(obj,item));
}
return builder.ToString();
}
}
到此表單就可以呈現到界面上了。
二、如何接收表單數據並綁定到動態模型上
在mvc中提供了數據綁定機制,可以快速的把表單數據綁定到對象上,但是現在我們的對象是動態的,那又該如何應對?
在表單操作中,當前對應的模型我們肯定知道,所以可以借助前面介紹的內容,我們先得到一個動態模型對象,具體操作如下:
//根據模型id獲取到type Type modelType = _runtimeModelProvider.GetType(modelid); //實例化 object obj = Activator.CreateInstance(modelType);
我們現在需要解決的是,如何把動態表單提交的數據綁定到obj的屬性上。方法也很簡單,在mvc中給我們提供了很好的支持,方法就是TryUpdateModelAsync,借助這個方法,就可以很方便的把數據綁定到obj上,具體調用實例
TryUpdateModelAsync(obj, modelType, "")
obj就是上面實例化的對象,modelType就是動態模型對應的Type信息,有了數據后,后面就是通過ef完成數據庫同步的事了,比如增加
ShopDbContext.Add(obj); ShopDbContext.SaveChanges();
所有代碼還是需要大家自己完善補充,如果有不足之處,歡迎大家能夠批評指正。
