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

时间:2018-11-28 20:06:23

标签: razor asp.net-core

我为验证消息的输出编写了一个自定义的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 个答案:

答案 0 :(得分:1)

总是出现此消息有两个原因。

  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. 其次,您忘记了检查当前属性是否已经有值。假设我们有一个装饰了[Required]属性的属性,并且确实分配了一个值,那么我们就不应该显示诸如“要求xxx”之类的消息。

此外,您的代码中还有null object reference的其他错误:

    var displayAttrib = memberExpression.Member
            .GetCustomAttributes(typeof(DisplayAttribute), false)    
            .Cast<DisplayAttribute>()
            .SingleOrDefault();                                      

    var errMsg = reqAttrib.ErrorMessage ?? displayAttrib.Name + " is required.";
  • reqAttrib可能是null,所以reqAttrib.ErrorMessage可能会抛出错误。
  • displayAttrib也可能是null
  • 如果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>");
    }

答案 1 :(得分:1)

非常感谢@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>