Search code examples
c#asp.net-coreerror-handlingrazor-pagesmodelstate

Is is any way to show errors in the corresponding form?


I have an ASP.NET Core 3.1 Razor Pages and MVC application. It is quite simple when it is only one form on a page, but how correctly show the errors when there are few forms?

Example: /ManageUser.cshtml

<div>
  <form method="post">
    <div class="validation-summary-valid text-danger" asp-validation-summary="All"></div>
    <div class="form-group">
      <label asp-for="OldPassword"></label>
      <input asp-for="OldPassword" class="form-control" />
    </div>
    <div class="form-group">
      <label asp-for="NewPassword1"></label>
      <input asp-for="NewPassword1" class="form-control" />
    </div>
    <div class="form-group">
      <label asp-for="NewPassword2"></label>
      <input asp-for="NewPassword2" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary" asp-page-handler="ChangePassword">Change password</button>
  </form>
</div>

<!-- let's say it is the other "tab" -->
<div>
  <form method="post">
    <div class="validation-summary-valid text-danger" asp-validation-summary="All"></div>
    <div class="form-group">
      <label asp-for="ChangeEmail"></label>
      <input asp-for="ChangeEmail" class="form-control" />
    </div>
    <button type="submit" class="btn btn-primary" asp-page-handler="ChangeEmail">Change email</button>
  </form>
</div>

And, if on the backend side any error is found:

public async Task<IActionResult> OnPostChangeEmailAsync([FromService] UserManager<User> userManager)
{
  //... skipped for brevity
  ModelState.AddModelError("", "User not found or deleted");

  return Page();
}

then this error is shown for both forms. Any way to show it for corresponding form only?

PS: Please do not propose to make the errors field-bound: it is not the case and the fields in the example is just for the simplicity.


Solution

  • Taking field validation as an example, you can try the following code.

    View:

        <div>
        <form method="post">
            <span class="text-danger">@Html.ValidationMessage("PasswordError")</span>
            <div class="form-group">
                <label asp-for="UserModel.OldPassword"></label>
                <input asp-for="UserModel.OldPassword" class="form-control" />
                <span asp-validation-for="UserModel.OldPassword" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="UserModel.NewPassword1"></label>
                <input asp-for="UserModel.NewPassword1" class="form-control" />
                <span asp-validation-for="UserModel.NewPassword1" class="text-danger"></span>
            </div>
            <div class="form-group">
                <label asp-for="UserModel.NewPassword2"></label>
                <input asp-for="UserModel.NewPassword2" class="form-control" />
                <span asp-validation-for="UserModel.NewPassword2" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary" asp-page-handler="ChangePassword">Change password</button>
        </form>
    </div>
    <div>
        <form method="post">
            <span class="text-danger">@Html.ValidationMessage("EmailError")</span>
            <div class="form-group">
                <label asp-for="UserModel.ChangeEmail"></label>
                <input asp-for="UserModel.ChangeEmail" class="form-control" />
                <span asp-validation-for="UserModel.ChangeEmail" class="text-danger"></span>
            </div>
            <button type="submit" class="btn btn-primary" asp-page-handler="ChangeEmail">Change email</button>
        </form>
    </div>
    

    The backend side(you can remove the attributes):

    public async Task<IActionResult> OnPostChangeEmailAsync(UserModel userModel)
        {
            
            if (!ModelState.IsValid)
            {
                ModelState.Remove("UserModel.OldPassword");
                ModelState.Remove("UserModel.NewPassword1");
                ModelState.Remove("UserModel.NewPassword2");
                ModelState.AddModelError("EmailError", "User not found or deleted");
            }
            return Page();
        }
        public async Task<IActionResult> OnPostChangePassword(UserModel userModel)
        {
           
            if (!ModelState.IsValid)
            {
                ModelState.Remove("UserModel.ChangeEmail");
                ModelState.AddModelError("PasswordError", "Password is inconsistent ");
            }
            return Page();
            
        }
    

    Result: enter image description here