How to show validation message in mvc4 razor

2019-02-11 00:19发布

问题:

I am newbie in MVC Razor and I want to implement validation message on textboxes. Here I'm creating some textbox dynamically as follows:

View Code:

foreach (var items in (IEnumerable<System.Data.DataRow>)Model.UsersOfList)
{

  @Html.TextBoxFor(m => m.LoginNameOfLoginInfoTab, new { @class = "textBox_LoginInfoAndPermission", @value = (Model.LoginNameOfLoginInfoTab = items["UserName"].ToString()), @id = ("txtUserLoginName" + Model.UsernameOfLoginInfoTab.Trim()) })

  @Html.ValidationMessageFor(m => m.LoginNameOfLoginInfoTab, null, new { @class = "ErrorMessage" })

  @Html.TextBoxFor(m => m.UsernameOfLoginInfoTab, new { @class = "textBox_LoginInfoAndPermission", @value = (Model.UsernameOfLoginInfoTab = items["FirstName"].ToString()), @id = ("txtUserName" + Model.UsernameOfLoginInfoTab.Trim()) })

  @Html.ValidationMessageFor(m => m.UsernameOfLoginInfoTab, null, new { @class = "ErrorMessage" })


}

in the module I've written code for validation as follows:

[Required (ErrorMessage="*")]
    public string UsernameOfLoginInfoTab
    {
        get;
        set;
    }


   [Required(ErrorMessage = "*")]
    public string LoginNameOfLoginInfoTab
    {
        get;
        set;
    }

Now when all textboxes has been created and when one validation message is displaying for first loop iteration textbox then it will automatically displaying in front of another textbox too which is created on second loop iteration.

Please tell me whats going wrong.

回答1:

The problem is because the expression you're using in TextBoxFor and ValidationMessageFor, which is used by MVC to create a string name for the field and look up validation messages from ModelState, is always the same throughout the iteration of the loop.

Your approach here seems a bit flawed, so my answer is more comprehensive.

1) Make view models that structurally represent the info you are trying to display.

Fix your view models:

public class UserInfoViewModel
{
    [Required (ErrorMessage="*")]
    public string UserName { get; set; }


   [Required(ErrorMessage = "*")]
    public string LoginName { get; set; }
}

// I don't know if you actually need this or not, but your existing model may contain additional properties relevant to the view that I don't know about, so I'll keep it.
public class ListOfUsersViewModel
{
    public IList<UserInfoViewModel> UsersOfList { get; set; }
}

Fix your action (I'm making this up here to illustrate a point):

public ActionResult ListOfUsers()
{
     var users = GetUserDataRows(); // gets your collection of DataRows
     var model = new ListOfUsersViewModel
                     {
                         UsersOfList = users.Select(row = new UserViewModel { UserName = row["FirstName"], LoginName = row["UserName"] }).ToList()
                     };

     return View(model);                  
}

2) Now you can iterate through users in your view and create proper fields with validation messages.

Let's call this view ListOfUsers.cshtml. Include whatever other things you need in your view, but use a for loop instead.

@using(Html.BeginForm("ListOfUsers"))
{
    <ul>
    @for (var i = 0; i < Model.UsersOfList.Count; i++)
    {
       <li>
       @Html.TextBoxFor(m.UsersOfList[i].LoginName, new {@class="textbox_LoginInfoAndPermission"})
       @Html.ValidationMessageFor(m => m.UsersOfList[i].LoginName)

       @Html.TextBoxFor(m.UsersOfList[i].UserName, new {@class="textbox_LoginInfoAndPermission"})
       @Html.ValidationMessageFor(m => m.UsersOfList[i].UserName)
       </li>
    }
    </ul>
   <button type="submit">Submit changes</button>
}

This will result in HTML like this for each item (0 in the name and id will be the index of the user in the collection):

<li>
<input type="text" id="UsersOfList_0_LoginName" name="UsersOfList[0].LoginName" value="..." />
<span class="field-validation-valid" data-valmsg-for="UsersOfList_0_LoginName" ... ></span>

<input type="text" id="UsersOfList_0_UserName" name="UsersOfList[0].UserName" value="..." />
<span class="field-validation-valid" data-valmsg-for="UsersOfList_0_UserName" ... ></span>
</li>

3) Create an action to receive submitted changes. This action will automatically bind the submitted values to the model argument, and do validation for you. All you need to do is check ModelState.IsValid.

[HttpPost, ActionName("ListOfUsers")]
public ActionResult ListOfUsersPost(ListOfUsersViewModel model)
{
    // at this point, model will be instantiated, complete with UsersOfList with values submitted by the user

    if (ModelState.IsValid) // check to see if any users are missing required fields. if not...
    {
         // save the submitted changes, then redirect to a success page or whatever, like I do below
        return RedirectToAction("UsersUpdated");
    }

    // if ModelState.IsValid is false, a required field or other validation failed. Just return the model and reuse the ListOfUsers view. Doing this will keep the values the user submitted, but also include the validation error messages so they can correct their errors and try submitting again
    return View("ListOfUsers", model);

}