Populate form fields with data using Html.EditorForModel in Model MVC

588 views Asked by At

When using the EditorForModel fields alongside validation and ModelState.IsValid, how to return to the original view but keep the populated fields?

When debugging, I can see the model I'm passing back into the view has the field data in there, but the text inputs etc do not contain the values.

What am I doing wrong?

Controller code as follows:

[Authorize]
public ActionResult ChangePassword()
{
  return View(new Views.PublicAuthChangePassword());
}

[Authorize]
[HttpPost]
[ValidateAntiForgeryToken]
[ActionName("ChangePassword")]
public ActionResult ChangePassword_Post(ChangePasswordVM vm)
{
  try
  {
      if (ModelState.IsValid)
      {
        MyUser user = new MyUser();
        bool changeResponse = MyUser.ChangePassword(vm.OldPassword, vm.NewPassword);                    

        if (changeResponse)
          ViewBag.Message = "Password changed successfully";
        else
          ViewBag.Message = "Unable to change password";
      }
    }
    catch (System.ArgumentNullException ex)
    {
      //Either old password or new password are null
    }
    catch (System.ArgumentException ex)
    {
      //Either old password or new password are empty string
    }
    catch (PlatformNotSupportedException ex)
    {
      //This method is not available. 
    }
    catch (Exception ex)
    {
      //An unknown error occurred
    }
  return View(vm);
}

View:

@model ChangePasswordVM

@using (Html.BeginForm("ChangePassword", "MyController", FormMethod.Post, new { @class = "change-password" }))
{
  <h2>Change My Password</h2>
  @Html.AntiForgeryToken()
  @Html.EditorForModel(Model)
  <input id="btnChangePassword" type="submit" value="Change password" />
}

Model:

public class ChangePasswordVM 
{
  [Required(ErrorMessage = "Old password is required")]
  [DataType(DataType.Password)]
  public string OldPassword { get; set; }

  [Required(ErrorMessage = "New password is required")]
  [DataType(DataType.Password)]
  public string NewPassword { get; set; }

  [Required(ErrorMessage = "Confirm password is required")]
  [DataType(DataType.Password)]
  [CompareAttribute("NewPassword", ErrorMessage = "Password doesn't match.")]
  public string ConfirmPassword { get; set; }
}

Thanks Simon

1

There are 1 answers

1
Fran On

I would flip your modelstate check.

public ActionResult ChangePassword_Post(PasswordViewModel vm)
{
   if (!ModelState.IsValid)
   {
      return View(vm);
   }
   try
   {
      MyUser user = new MyUser();
      bool changeResponse = MyUser.ChangePassword(vm.OldPassword, vm.NewPassword);                    

      if (changeResponse)
        ViewBag.Message = "Password changed successfully";
      else
        ViewBag.Message = "Unable to change password";

    }
    catch (System.ArgumentNullException ex)
    {
      //Either old password or new password are null
    }
    catch (System.ArgumentException ex)
    {
      //Either old password or new password are empty string
    }
    catch (PlatformNotSupportedException ex)
    {
      //This method is not available. 
    }
    catch (Exception ex)
    {
      //An unknown error occurred
    }

    ViewBag.Message = "Unable to change password";
    return View(vm);
}

This will return your model validation errors to your view if you modelstate check fails. It will also re-populate any fields with your viewmodel values.

If you have any dropdowns, you would need to explicitly re-create the data and pass it back.

Your also going to have to set your ViewBag.Message again at the end because you only set it if the call falls without an exception thrown.

You should also be following the PRG pattern and Redirecting to a display or index page and not the same view.