【问题标题】:MVC Client-Side Validation for EditorFor in foreachforeach 中 EditorFor 的 MVC 客户端验证
【发布时间】:2015-10-11 14:14:10
【问题描述】:

所以我有一个具有以下结构的视图(这不是实际代码,而是一个摘要):

@using (Html.BeginForm("Action", "Controller", FormMethod.Post))
{
  @Html.ValidationSummary("", new { @class = "text-danger" })
  <table>
    <thead>
      <tr>
        <th>Column1</th>
        <th>Column2</th>
      </tr>
    </thead>
    <tbody id="myTableBody">
      @for (int i = 0; i < Model.Components.Count; i++)
      {
        @Html.EditorFor(m => m.MyCollection[i])
      }
    </tbody>
    <tfoot>
      <tr>
        <td>
          <button id="btnAddRow" type="button">MyButton</button>
        </td>
      </tr>
    </tfoot>
  </table>

  <input type="button" id="btnSubmit" />
}

@section scripts {
  @Scripts.Render("~/Scripts/MyJs.js")
}

EditorFor 正在呈现表示绑定到 MyCollection 中属性的行的标记。以下是编辑器模板外观的示例 sn-p:

@model MyProject.Models.MyCollectionClass

<tr>
  <td>
    @Html.TextBoxFor(m => m.Name)
  </td>
  <td>
    @Html.DropDownListFor(m => m.Type, Model.AvailableTypes)
  </td>
</tr>

基本上,我的问题是客户端验证不会触发编辑器模板内的元素,因为它应该。有人可以指出我可能会出错的正确方向吗?

另外,请注意我的 web.config 中设置了以下内容。

<appSettings>
  <add key="ClientValidationEnabled" value="true" />
  <add key="UnobtrusiveJavaScriptEnabled" value="true" />
</appSettings>

而且 MyCollectionClass 对应该强制执行的属性有适当的 [Require] 注释。还有一点需要注意的是检查

if(ModelState.IsValid)
{
}

如果必填字段不正确,则按预期返回 false。那里的问题是我想要客户端验证而不是服务器端。我的其他页面之一是实现 jQuery 验证,但不包含此场景所做的所有嵌套,因此它可以正常工作。

提前致谢。

【问题讨论】:

  • 您是否添加了 jquery 不显眼的验证脚本?我可以查看您页面中包含的脚本吗?
  • 删除 for 循环 - 它只需要是 @Html.EditorFor(m =&gt; m.MyCollection) - EditorFor() 方法接受 IEnumerable&lt;T&gt; 并且足够聪明,可以为集合中的每个项目呈现正确的 html。但是您的代码没有意义,因为for 循环是针对属性Components 而不是属性MyCollection(这是一个错字吗?)
  • 在任何情况下,您都应该在EditorTemplate 中为每个属性添加@Html.ValidationMessageFor(),以便用户清楚地知道哪一行有错误(然后摘要应该是@Html.ValidationSummary(true, "", new { @class = "text-danger" })
  • Stephen 提到,EditorTemplates 会自动迭代集合。你不这样做,也不应该自己做,除非你有一个非常具体的理由这样做(你的代码没有表明是这种情况)。这不仅使您的 html 更整洁,而且将确保为模板上下文完成所有正确的连接,并且正确命名字段名称。
  • @StephenMuecke 这是我的错字。我试图使用通用名称(即 MyCollection),但最终输入了实际的集合(即组件)。感谢您和其他人让我知道 @Html.EditorFor() 足够聪明,可以遍历 IEnumerable。我现在在家,代码正在工作,所以我会在早上验证这是否解决了我的问题。希望这些建议之一可以解决问题。

标签: javascript jquery asp.net-mvc validation model-view-controller


【解决方案1】:

据我所知,MVC 并没有真正提供“开箱即用”的客户端验证。 有第三方选项,但我更喜欢自己做,所以我用 JavaScript 手工完成了整个事情。 与 EditorFor 复合不允许像其他 Helper 方法那样添加 html 属性。

我对此的修复相当复杂,但我感觉很全面,我希望你发现它和我一样有帮助

首先在.Net中重载HtmlHelper EditorFor

public static HtmlString EditBlockFor<T, TValue>(this HtmlHelper<T> helper, Expression<System.Func<T, TValue>> prop, bool required)
    {   
        string Block = "";
        Block += "<div class='UKWctl UKWEditBox' " +
            "data-oldvalue='" + helper.ValueFor(prop) + "' " +
            "data-required='" + required.ToString() + ">";
        Block += helper.EditorFor(prop);
        Block += "</div>";

        return new HtmlString(Block);
    }

在 razor 视图中添加新的 editBlockfor(就像您一样),但更改 Begin Form 方法以将名称和 id 添加到表单元素,以便您以后可以识别它

    @using (Html.BeginForm("Action", "Controller", FormMethod.Post, new { name = "MyDataForm", id = "MyDataForm" }))

然后当用户点击保存时,从 JavaScript 运行验证方法

function validate(container)    {

    var valid = true;
    //use jquery to iterate your overloaded editors controls fist and clear any old validation
    $(container).find(".UKWctl").each(function (index) { clearValidation($(this)); });
    //then itterate Specific validation requirements 
    $(container).find(".UKWctl[data-required='True']").each(function (index) {
        var editobj = getUKWEdit(this);
        if (editobj.val() == "") {
            valid = false;
            //use this Method to actually add the errors to the element
            AddValidationError(editobj, 'This field, is required');
            //now add the Handlers to the element to show or hide  the valdation popup
            $(editobj).on('mouseenter', function (evt) { showvalidationContext(editobj, evt); });
            $(editobj).on('mouseout', function () { hidevalidationContext(); });
            //finally add a new class to the element so that extra styling can be added to indicate an issue 
        $(editobj).addClass('valerror');
    }
    });
    //return the result so the methods can be used as a bool
    return valid;
}

添加验证方法

function AddValidationError(element, error) {
    //first check to see if we have a validation attribute using jQuery
    var errorList = $(element).attr('data-validationerror');
    //If not Create a new Array() 
    if (!errorList || errorList.length < 1) {
        errorList = new Array();
    } else {
        //if we have, parse the Data from Json
        var tmpobj = jQuery.parseJSON(errorList);
        //use jquery.Map to convert it to an Array()
       errorList = $.map(tmpobj, function (el) { return el; });
    }
   if ($.inArray(error, errorList) < 0) {
        // no point in add the same Error twice (just in case)
        errorList.push(error);
    }
    //then stringyfy the data backl to JSON and add it to a Data attribute     on your element using jQuery
     $(element).attr('data-validationerror', JSON.stringify(errorList));
}

最后显示和隐藏实际的错误, 为了方便这一点,我在 _Layout.html 中添加了一个小 div 元素

    <div id="ValidataionErrors" title="" style="display:none">
        <h3 class="error">Validation Error</h3>
        <p>This item contatins a validation Error and Preventing Saving</p>
        <p class="validationp"></p>
    </div>

显示

var tipdelay;
function showvalidationContext(sender, evt)
{
    //return if for what ever reason the validationError is missing
    if ($(sender).attr('data-validationerror') == "") return;
    //Parse the Error to an Object 
    var jsonErrors = jQuery.parseJSON($(sender).attr('data-validationerror'));
    var errorString = '';
//itterate the Errors from the List and build an 'ErrorString'
    for (var i = 0; i <= jsonErrors.length; i++)
    {
        if (jsonErrors[i]) {
            //if we already have some data slip in a line break
            if (errorString.length > 0) { errorString += '<br>'; }
            errorString += jsonErrors[i];
        }
    }
//we don't want to trigger the tip immediatly so delay it for just a moment 
    tipdelay = setTimeout(function () {
        //find the p tag tip if the tip element  
        var validationError = $('#ValidataionErrors').find('.validationp');
        //then set the html to the ErrorString 
        $(validationError).html(errorString);
        //finally actually show the tip using jQuery, you can use the     evt to find the mouse position 
        $('#ValidataionErrors').css('top', evt.clientY);
        $('#ValidataionErrors').css('left', evt.clientX);
        //make sure that the tip appears over everything 
        $('#ValidataionErrors').css('z-index', '1000');
        $('#ValidataionErrors').show();
    }, 500);
}    

隐藏(更容易隐藏)

function hidevalidationContext() {
    //clear out the tipdelay
    clearTimeout(tipdelay);
    //use jquery to hide the popup
    $('#ValidataionErrors').css('top', '-1000000px');
    $('#ValidataionErrors').css('left', '-1000000px');
    $('#ValidataionErrors').css('z-index', '-1000');
    $('#ValidataionErrors').hide();
}

你可以尝试一些类似的东西

function save()
{
    if (validate($("#MyDataForm")))
    {
         $("#MyDataForm").submit();   
    }
    else {
        //all the leg has been done this stage so perhaps do nothing
    }
}

这是用于验证弹出窗口的我的 Css

#ValidataionErrors {
    display: block; 
    height: auto;
    width: 300px;
    background-color: white;
    border: 1px solid black;
    position: absolute;
    text-align: center;
    font-size: 10px;
}

#ValidataionErrors h3 { border: 2px solid red; }
.valerror { box-shadow: 0 0 2px 1px red; }

【讨论】:

    猜你喜欢
    • 2012-07-29
    • 2013-12-12
    • 1970-01-01
    • 2010-09-14
    • 2014-07-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多