【问题标题】:Unobtrusive Validation on PostBack Created ClassPostBack 创建类的不显眼验证
【发布时间】:2015-09-28 13:32:34
【问题描述】:

我在让我的请求表进行人员证明时遇到了一点麻烦,所以它只能提交有效信息并告诉用户如果没有则错误是什么。

我希望控制器创建的新属性(新联系人)在表单回发时被不显眼的 JS 验证为 false。

重要 - 这是使用BeginCollectionItem 从属性cmp.contacts (List<Contact>) 动态添加和删除Contact 对象。

这些属性是必需的,但可以从表单中删除。提交后,控制器会检查它们,如果不存在,它会将它们重新添加到表单中。

由于刚刚添加的属性,它们在技术上并不无效。

更新 - 进一步调查

但是,这看起来确实很奇怪,因为我已将 "" 作为字符串值无效,因为在模型中设置的字符长度至少为 3。所以在回发的控制器中,我检查列表是否为空,它是(如果用户已删除它)并创建一个带有新联系人的新列表,然后通过ModelState.IsValid 检查,它通过。联系人的属性是“”的字符串值,应该是无效的,但直到第二次回帖才将它们标记为无效,这是为什么呢?

示例故事:

用户从页面中删除联系人列表(您可以有 1 个以上的联系人),提交表单,表单失败,因为它至少需要一个联系人。控制器创建一个新联系人并将表单发送回表单。页面显示有联系人属性,但由于是新创建的,并不无效。

如果用户尝试再次提交它们,它们将被标记为无效,但我希望用户需要填写的内容非常明显。

我可以在回发时将它们设置为无效吗?

带有问题字段的部分视图:

@model Contact
<div class="editorRow">
    @using (HtmlHelpers.BeginCollectionItem.HtmlPrefixScopeExtensions.BeginCollectionItem(Html, "contacts"))
    {
        <div class="ui-grid-c ui-responsive">
            <div class="ui-block-a">
                <span>
                    @Html.TextBoxFor(m => m.name)
                </span>
            </div>
            <div class="ui-block-b">
                <span>
                    @Html.TextBoxFor(m => m.telephone)
                </span>
            </div>
            <div class="ui-block-c">
                <span>
                    @Html.TextBoxFor(m => m.email)
                </span>
            </div>
            <div class="ui-block-d">
                <span>
                    @Html.DropDownListFor(m => m.type, new List<SelectListItem>
               {
                   new SelectListItem { Text = "Admin", Value = "Admin" },
                   new SelectListItem { Text = "Peer", Value = "Peer" },
                   new SelectListItem { Text = "Technical", Value = "Technical" }
               })
            <span class="dltBtn">
                <a href="#" class="deleteRow">
                    <img src="~/Images/DeleteRed.png" style="width: 15px; height: 15px;" />
                </a>
            </span>
        </span>
    </div>
</div>
    }
</div>

控制器

public ActionResult Create()
        {
            var cmp = new Company
            {
                contacts = new List<Contact>
                {
                    new Contact { email = "", name = "", telephone = "", type = "" }
                }
            };
            return View(cmp);
        }

[ValidateAntiForgeryToken]
        public ActionResult Create(Company cmp)
        {
            if (ModelState.IsValid)
            {
                db.companys.Add(cmp);
                db.SaveChanges();
                EmailSupport.SendEmail(cmp, "Request Form");
                return RedirectToAction("Thankyou", "Home");
            }

            if (cmp.contacts == null)
                cmp.contacts = new List<Contact>
                {
                    new Contact { email = "", name = "", telephone = "" } // this row of properties to show required
                };

            return View(cmp);
        }

型号

[Table("Company")]
    public class Company
    {
        [Key]
        public int companyId { get; set; }
        [Required(ErrorMessage="Company name required.")]
        public string name { get; set; }
        [Required(ErrorMessage = "Telephone required.")]
        public string telephone { get; set; }
        [Required(ErrorMessage="Registration Number required.")]
        public string regNumber { get; set; }
        [EnsureOneItem]
        public List<Contact> contacts { get; set; }
    }
    [Table("Contact")]
    public class Contact
    {
        [Key]
        public int contactId { get; set; }
        public int companyId { get; set; }
        [ForeignKey("companyId")]
        public Company company { get; set; }
        [Required(ErrorMessage="Contact name required.")]
        public string name { get; set; }
        [Required(ErrorMessage="Telephone required.")]
        public string telephone { get; set; }
        [Required]
        [RegularExpression(@"^([a-zA-Z0-9_\-\.]+)@((\[[0-9]{1,3}" +
                            @"\.[0-9]{1,3}\.[0-9]{1,3}\.)|(([a-zA-Z0-9\-]+\" +
                            @".)+))([a-zA-Z]{2,4}|[0-9]{1,3})(\]?)$",
                            ErrorMessage = "Email is not valid.")]
        public string email { get; set; }
        [Required(ErrorMessage = "Contact type required.")]
        public string type { get; set; }
    }

【问题讨论】:

  • @StephenMuecke if(cmp.contacts == null) 不会失败,当调试集合为空时,不确定为什么或我在其他实现领域做错了什么。我已经更新了这个问题,并将通过进一步的调查继续这样做 - 我已经从我的控制器中包含了 create 方法,我将 cmp 对象传递给页面。删除 contacts (List&lt;Contact&gt;) 的唯一行会使 contacts null 而不是 POST 上的空集合。
  • 刮掉我的最后一条评论。 TryUpdateModel() 不起作用,因为新的 Contact 的值不在请求中。您将需要创建一个新的ValidationContext。我会尽快添加答案
  • 谢谢 - 你能否解释一下为什么当我在控制器中实例化 null 并将对象传递给表单时,我将其返回为 List&lt;Contact&gt;,然后将其序列化为 html 为根据我的理解,并在提交时发回 - 如果至少有 contacts[0] 联系人在发布时不会是 null
  • 在您的情况下您将获得null,因为您没有在Contact 的构造函数中初始化集合。但是这样做很典型,所以我建议还测试cmp.contacts 是否包含任何项目

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


【解决方案1】:

您需要创建自己的ValidationContext 来验证新的Contact 对象,并将结果添加到ModelState

if (cmp.contacts == null || !cmp.contacts.Any())
{
  var newContact = new Contact(); // no need to set the values to empty strings
  cmp.contacts = new List<Contact>{ newContact };

  // validate
  var context = new ValidationContext(newContact);
  var results = new List<ValidationResult>();
  Validator.TryValidateObject(newContact, context, results);

  // add errors to ModelState
  foreach(var result in results)
  {
    var propertyName = string.Format("contacts[0].{0}", result.MemberNames.First());
    ModelState.AddModelError(propertyName, result.ErrorMessage);
  }

  return View(cmp);
}

【讨论】:

  • 感谢您的回答,现在尝试,第一次没有工作。至于cmp.contacts,如果集合中有联系人对象,它确实包含联系人对象,否则为null。顺便说一句,我没有任何错误消息,我只是用它来用红色突出显示需要验证的框。仍然做同样的事情,只验证页面上联系人中添加的联系人行,如果他们在帖子中。
  • 如果您遇到问题,请告诉我,我会为您创建一个 DotNetFiddle(在我的机器上测试并且工作正常),但您仍然需要添加 ModelState 错误,即使您不这样做'不显示它们
  • 只是调查您在答案中使用的方法。出现奇怪的 SO 错误...如果可以的话,那将非常有帮助。我没有使用Html.ValidationMessageFor,因为我不想要消息,只想要文本框红色边框但使用模型指定的数据注释验证。由于之前的 qs,您肯定已经知道,这是使用 BeginCollectionItem。
  • 哦,我忘记了你之前的问题。这可能会产生影响(匹配错误)。我没有使用BeginCollectionItem,所以我无法测试它,但让我考虑一下
  • 对不起!应该提到,我会更新 Q,所以当删除集合中的最后一项时,使用 AJAX 删除它然后不会验证模型返回 false 时添加的新元素(至少需要一个联系人)所以使用新创建的 List&lt;Contact&gt;{new Contact();} 回帖 - 如果您需要更多代码,请告诉我,感谢您的帮助。
【解决方案2】:

您可以使用ValidateModel()TryValidateModel() 如下所示

  if (cmp.contacts == null)
cmp.contacts = new List<Contact>{
                new Contact { email = "", name = "", telephone = "" }
            };
  foreach(Contact cont  in cmp.contacts){
          if(!TryValidateModel(cont)){
            ModelState.AddModelError("", "An Invalid Contact Were Found!");
 //       return View(cmp); or
 return View("InvalidContact",cont);
     //you need to create a view nammed InvalidContact to provide more validation error messages
           }
  }

InvalidContact.cshtml

              @model  proj.Models.Contact

                @Html.ValidationSummary(true)
                 <div class="col-md-10">
                        <div class="input-group"> 
                            @Html.TextBoxFor(m => m.telephone , new { @class = "form-control", placeholder = "telephone " })
                            @Html.ValidationMessageFor(m => m.telephone )
                        </div>
                    </div>...

【讨论】:

  • 谢谢 我会测试一下,虽然添加更多页面并不理想,但不知道如何工作,只突出显示红色框 - 没有办法添加CSS类从后端到对象属性?我还尝试将我的控制器切换到 if(ModelState.IsValid)if(null) 后面,但这也没有验证它们。
  • 我不明白你的意思亲爱的@PurpleSmurph 这是一个 xy 问题吗?可能是!如果有帮助,请考虑接受答案...
  • 对不起,我的意思是,是不是不能通过控制器给一个对象(本例是联系人)添加一个css类?
【解决方案3】:

不幸的是,我尝试并未能获得我想要的结果,回发验证,但结果过于复杂。

这更好(更简单)并且与我想象的更多人的问题更相关 - 感谢与斯蒂芬的讨论和建议。

如果长度仅为 1,则使用简单的 jQuery 删除删除按钮。

var conLen = $('#editorRowsContact .editorRow').length;
            if (conLen == 1) {
                $('#editorRowsContact .editorRow .deleteRow').remove();
            }

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-06-18
    • 2016-02-19
    相关资源
    最近更新 更多