动态创建视图:将属性元数据填充到嵌套视图以进行客户端验证?

时间:2016-10-13 10:38:36

标签: c# .net asp.net-mvc validation

我已经为系统中不同模块的配置表单实现了基本视图模型。

在该基本逻辑中,我正在迭代(user-)派生的viewmodel的反射属性,并动态创建ConfigItem对象列表

简化我的对象如下所示:

public class ConfigItem
{ 
     public string Key { get; set; }
     public object Value { get; set; }
     public  Type PropertyType { get; set; } 
     public ConfigType ConfigType { get; set; }
     public string PostId { get; set; } 
};

“value”属性可以是简单或复杂类型的对象(字符串,bool图像等)。

在嵌套视图中,我在循环中渲染所有ConfigItems(为了用工具提示等附加信息来装饰每个ConfigItems)。 对于已发布表单的绑定,我将设置输入字段的name属性,以便将值绑定回原始的viewmodel属性。 DataAnnotation属性的验证适用于服务器端。

现在问题:

众所周知,ASP具有渲染jquery不显眼的验证属性的简洁功能。不幸的是,在渲染ConfigItems而不是原始属性时,我丢失了所有元数据,因此ASP不知道我的属性,例如“必需”或“StringLength”。有没有人知道如何“携带”属性元数据?

最好的是我的ConfigItem的值可能是类型为Expression>的表达式树,所以我可以携带一个指向原始属性的“指针”。但我无法弄清楚如何从技术上解决它。如果有人可以对此有所了解(或者可能还有其他一些提示),我非常感谢。谢谢!

更新: 我用一个小的自定义Html-Helper渲染我的ConfigItems,在那里我评估模型状态:

public static MvcHtmlString ConfigItem<TModel>(this HtmlHelper<TModel> htmlHelper, ConfigItem configItem, bool isEditMode = false)
    {
        if (isEditMode)
        {
            if (!htmlHelper.ViewData.ModelState.IsValidField(configItem.PostId))
            {
                var itemState = htmlHelper.ViewData.ModelState[configItem.PostId];

                if (itemState.Errors.Count > 0)
                {
                    configItem.ValidationMessage = string.Join("</br>", Array.ConvertAll(itemState.Errors.ToArray(), i => i.ErrorMessage));
                }
            }
            return htmlHelper.Partial(EditorConfigItemPartialPath, configItem);
        }
        else
        {
            return htmlHelper.Partial(DisplayConfigItemPartialPath, configItem);
        }
    }

ConfigItem EditorTemplate做了类似这样的事情:

@model ConfigItem    
<div class="value">
@switch (Model.DataType)
{
    case ConfigDataType.String:
        {
            var value = Model.Value == null ? "" : Model.Value;
            @Html.EditorFor(m => value, "string", Model.PostId)
            break;
        }
    case ConfigDataType.Bool:
        {
            @Html.EditorFor(m => Model.Value, "", Model.PostId)
            break;
        }
    case ConfigDataType.Image:
        {
            @Html.ConfigImage(Model.Key, m => Model.Value, Url.Action(ApplicationController.ActionUploadImage), Model.PostId)
            break;
        }
}
@if (!string.IsNullOrEmpty(Model.ValidationMessage))
{
    <span class="@errorClass">
        @Html.Raw(Model.ValidationMessage)
    </span>
}
</div>

1 个答案:

答案 0 :(得分:0)

如果您错过了部分视图的脚本,则可以使用以下代码:

将脚本加载到局部视图中的常用功能:

function loadScript(url, callback){

    var script = document.createElement("script")
    script.type = "text/javascript";

    if (script.readyState){  //IE
        script.onreadystatechange = function(){
            if (script.readyState == "loaded" ||
                    script.readyState == "complete"){
                script.onreadystatechange = null;
                callback();
            }
        };
    } else {  //Others
        script.onload = function(){
            callback();
        };
    }

    script.src = url;
    document.getElementsByTagName("head")[0].appendChild(script);
}

并将此函数调用到您的switch语句中,如下所示:

@switch (Model.DataType)
{
    case ConfigDataType.String:
        {
            var value = Model.Value == null ? "" : Model.Value;
            @Html.EditorFor(m => value, "string", Model.PostId)
            loadScript("~/Content/js/register.js", function(){
            //initialization code
            });
            break;
        }
    case ConfigDataType.Bool:
        {
            @Html.EditorFor(m => Model.Value, "", Model.PostId)
            loadScript("~/Content/js/register.js", function(){
            //initialization code
            });
            break;
        }
    case ConfigDataType.Image:
        {
            @Html.ConfigImage(Model.Key, m => Model.Value, Url.Action(ApplicationController.ActionUploadImage), Model.PostId)
            loadScript("~/Content/js/register.js", function(){
            //initialization code
            });
            break;
        }
}

~/Content/js/register.js替换为您所需的JavaScript

希望它会对你有所帮助。

由于