代码之家  ›  专栏  ›  技术社区  ›  Fraze

用于验证消息的自定义HTML帮助程序

  •  1
  • Fraze  · 技术社区  · 7 年前

    我为验证消息的输出编写了一个定制的HTTML助手标记。它工作正常,但它始终存在。

    @Html.MyValidationMsg(m => m.FirstName)

    public static IHtmlContent MyValidationMsg<TModel, TProperty>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
        {
            MemberExpression memberExpression = expression.Body as MemberExpression;
    
            var reqAttrib = memberExpression.Member
                          .GetCustomAttributes(typeof(RequiredAttribute), false)
                          .Cast<RequiredAttribute>()
                          .SingleOrDefault();
    
            var displayAttrib = memberExpression.Member
                          .GetCustomAttributes(typeof(DisplayAttribute), false)
                          .Cast<DisplayAttribute>()
                          .SingleOrDefault();
    
            var errMsg = reqAttrib.ErrorMessage ?? displayAttrib.Name + " is required.";
    
            var content = new HtmlContentBuilder()
                     .AppendHtml("<div class=\"rvt-inline-alert rvt-inline-alert--standalone rvt-inline-alert--danger\">")
                     .AppendHtml("<span class=\"rvt-inline-alert__icon\">")
                     .AppendHtml("<svg aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\">")
                    .AppendHtml("<g fill=\"currentColor\">")
                    .AppendHtml("<path d=\"M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,14a6,6,0,1,1,6-6A6,6,0,0,1,8,14Z\" />")
                    .AppendHtml("<path d=\"M10.83,5.17a1,1,0,0,0-1.41,0L8,6.59,6.59,5.17A1,1,0,0,0,5.17,6.59L6.59,8,5.17,9.41a1,1,0,1,0,1.41,1.41L8,9.41l1.41,1.41a1,1,0,0,0,1.41-1.41L9.41,8l1.41-1.41A1,1,0,0,0,10.83,5.17Z\"/>")
                    .AppendHtml("</g>")
                    .AppendHtml("</svg>")
                    .AppendHtml("</span>")
                    .AppendHtml("<span class=\"rvt-inline-alert__message\" role=\"alert\" id=\"radio-list-message\">")
                    .AppendHtml(errMsg)
                    .AppendHtml("</span>")
                    .AppendHtml("</div>");
            return content;
        }
    

    谢谢你的帮助

    2 回复  |  直到 7 年前
        1
  •  1
  •   itminus    7 年前

    1. 正如@Sonal Borkar所说,第一个原因是你的 var errMsg = reqAttrib.ErrorMessage ?? displayAttrib.Name + " is required." 使它返回一些值,即使没有 [Required] 属性完全没有文档化 .

      但不要把它改成 var errMsg = reqAttrib.ErrorMessage != null ? displayAttrib.Name + " is required." ,因为 reqAttrib 可能是 null

      var errMsg = reqAttrib==null ? 
          "": 
          reqAttrib?.ErrorMessage ?? displayName + " is required.";
      
    2. 第二,你忘了 . 假设我们有一个带有 [必需]

    null object reference 在代码中:

        var displayAttrib = memberExpression.Member
                .GetCustomAttributes(typeof(DisplayAttribute), false)    
                .Cast<DisplayAttribute>()
                .SingleOrDefault();                                      
    
        var errMsg = reqAttrib.ErrorMessage ?? displayAttrib.Name + " is required.";
    
    • 这个 reqAttrib 无效的 ,所以 reqAttrib.ErrorMessage 可能会抛出错误。
    • 这个 displayAttrib 无效的
    • 如果 显示属性 是空的,我们应该退回到 memberExpression.Member.Name

    要修复错误,请按以下方式更改代码:

        public static IHtmlContent MyValidationMsg<TModel, TProperty>(this IHtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression)
        {
            MemberExpression memberExpression = expression.Body as MemberExpression;
    
            var reqAttrib = memberExpression.Member
                        .GetCustomAttributes(typeof(RequiredAttribute), false)
                        .Cast<RequiredAttribute>()
                        .SingleOrDefault();
    
            var contentBuilder = new HtmlContentBuilder();
            // if the current property has no [Required] attribute, there's no need to display required message 
            if(reqAttrib == null ){return contentBuilder; }
    
    
            // check the value of current property 
            var compiled = expression.Compile();
            var model = (TModel) helper.ViewData.Model ;
            if(model == null){ 
                throw new Exception("No Model associated with the view !"); 
            }
            var propValue = (TProperty) compiled.Invoke(model);        
            // I just test nullable props here, you might custom it to fulfill your requirements, eg : whether the length of string matches
            if(propValue != null){ return contentBuilder; }
    
    
            var member = memberExpression.Member;
            var displayName= member
                        .GetCustomAttributes(typeof(DisplayAttribute), false)
                        .Cast<DisplayAttribute>()
                        .SingleOrDefault()
                        ?.Name             // might be null
                        ?? member.Name;    // fall back
            var errMsg = reqAttrib==null ? 
                "": 
                reqAttrib?.ErrorMessage ?? displayName + " is required.";
            if(String.IsNullOrEmpty(errMsg)){ return contentBuilder; }
            return contentBuilder
                    .AppendHtml("<div class=\"rvt-inline-alert rvt-inline-alert--standalone rvt-inline-alert--danger\">")
                    .AppendHtml("<span class=\"rvt-inline-alert__icon\">")
                    .AppendHtml("<svg aria-hidden=\"true\" xmlns=\"http://www.w3.org/2000/svg\" width=\"16\" height=\"16\" viewBox=\"0 0 16 16\">")
                    .AppendHtml("<g fill=\"currentColor\">")
                    .AppendHtml("<path d=\"M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,14a6,6,0,1,1,6-6A6,6,0,0,1,8,14Z\" />")
                    .AppendHtml("<path d=\"M10.83,5.17a1,1,0,0,0-1.41,0L8,6.59,6.59,5.17A1,1,0,0,0,5.17,6.59L6.59,8,5.17,9.41a1,1,0,1,0,1.41,1.41L8,9.41l1.41,1.41a1,1,0,0,0,1.41-1.41L9.41,8l1.41-1.41A1,1,0,0,0,10.83,5.17Z\"/>")
                    .AppendHtml("</g>")
                    .AppendHtml("</svg>")
                    .AppendHtml("</span>")
                    .AppendHtml("<span class=\"rvt-inline-alert__message\" role=\"alert\" id=\"radio-list-message\">")
                    .AppendHtml(errMsg)
                    .AppendHtml("</span>")
                    .AppendHtml("</div>");
        }
    
        2
  •  1
  •   Fraze    7 年前

    非常感谢@itminus

    我最终完全是为了重写jquery.validate而划出了这条路径。还使用javascript/jquery操纵DOM以获得所需的结果。

    错误消息的所需输出:

    <div class="rvt-inline-alert rvt-inline-alert--danger">
        <span class="rvt-inline-alert__icon">
            <svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
                <g fill="currentColor">
                    <path d="M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,14a6,6,0,1,1,6-6A6,6,0,0,1,8,14Z"/>
                    <path d="M10.83,5.17a1,1,0,0,0-1.41,0L8,6.59,6.59,5.17A1,1,0,0,0,5.17,6.59L6.59,8,5.17,9.41a1,1,0,1,0,1.41,1.41L8,9.41l1.41,1.41a1,1,0,0,0,1.41-1.41L9.41,8l1.41-1.41A1,1,0,0,0,10.83,5.17Z"/>
                </g>
            </svg>
        </span>
        <span class="rvt-inline-alert__message" role="alert">Your Name is required.</span>
    </div>
    

    编码 _ValidationScriptsPartial.cshtml 要想变魔术:

    <script type="text/javascript">
        var settings = {
            errorElement: "span",
            errorClass: "rvt-validation-danger", //around textbox on error
            errorPlacement: function (error, element) {
    
                var avg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
                avg.setAttribute("width", "16");
                avg.setAttribute("height", "16");
                avg.setAttribute("viewBox", "0 0 16 16");
                avg.setAttribute("aria-hidden", "true");
    
                var g = document.createElement("g");
                g.setAttribute("fill", "currentColor");
    
                var p1 = document.createElement("path");
                p1.setAttribute("d", "M8,0a8,8,0,1,0,8,8A8,8,0,0,0,8,0ZM8,14a6,6,0,1,1,6-6A6,6,0,0,1,8,14Z");
    
                var p2 = document.createElement("path");
                p2.setAttribute("d", "M10.83,5.17a1,1,0,0,0-1.41,0L8,6.59,6.59,5.17A1,1,0,0,0,5.17,6.59L6.59,8,5.17,9.41a1,1,0,1,0,1.41,1.41L8,9.41l1.41,1.41a1,1,0,0,0,1.41-1.41L9.41,8l1.41-1.41A1,1,0,0,0,10.83,5.17Z");
    
                g.appendChild(p1);
                g.appendChild(p2);
                avg.appendChild(g);   
    
                var spanIcon = document.createElement("span");
                spanIcon.setAttribute("class", "rvt-inline-alert__icon");
                spanIcon.innerHTML += avg.outerHTML;
    
                var spanMsg = document.createElement("span");
                spanMsg.setAttribute("class", "rvt-inline-alert__message");
                spanMsg.setAttribute("role", "alert");
                spanMsg.innerHTML += error[0].innerHTML;           
    
                var c = document.createElement("div");
                c.setAttribute("class", "rvt-inline-alert rvt-inline-alert--danger");
                c.innerHTML += spanIcon.outerHTML + spanMsg.outerHTML;
                error.replaceWith(c);
            }
        };
    
        $.validator.unobtrusive.options = settings;
    </script>
    
    推荐文章