The scenario is a form when a user initially goes to a page the form is presented with all fields disabled and an edit button. Clicking on the edit button enables the form fields. However when this happens, for some reason, even though the form fields have data in them populated via a value attribute, they still show up as failing validation, and even deleting the text and readding it (in the form) doesn't "reset" the validation. I even tried to remove the validation errors, but if I do this and hit the "save" button that is then visible in the editable form which takes me to another Action method in the Pagemodel, it reports that the form fields are invalid and won't submit an update.
Here is code some of the front-end code:
<div asp-validation-summary="All"></div>
<div class="gcContent">
<div id="FormPnl">
<form asp-page-handler="post" method="post">
<input type="hidden" asp-for="MyInformation.Id" />
<input type="hidden" asp-for="MyInformation.UserName" />
<input type="hidden" asp-for="MyInformation.Email" />
<DataAnnotationsValidator />
<div class="validation" role="alert" asp-validation-summary="All">You received the following errors:</div>
<p>Enter User Information</p>
<p class="formAlerts">* Denotes a Required Field</p>
<label asp-for="MyInformation.FirstName">First Name:* </label>
<input @(Model.EditMode ? "" : "disabled") asp-for="MyInformation.FirstName" value="@Model.MyInformation.FirstName" class="form-control" />
<span asp-validation-for="MyInformation.FirstName"></span>
<label asp-for="MyInformation.LastName">Last Name:* </label>
<input @(Model.EditMode ? "" : "disabled") asp-for="MyInformation.LastName" value="@Model.MyInformation.LastName" class="form-control ">
<span asp-validation-for="MyInformation.LastName"></span>
<label asp-for="MyInformation.PhoneNumber">Phone:* </label>
<input @(Model.EditMode ? "" : "disabled") id="phone" asp-for="MyInformation.PhoneNumber" value="@Model.MyInformation.PhoneNumber" class="form-control ">
<span asp-validation-for="MyInformation.PhoneNumber"></span>
<label asp-for="MyInformation.PhoneExt">Phone Ext: </label>
<input @(Model.EditMode ? "" : "disabled") asp-for="MyInformation.PhoneExt" value="@Model.MyInformation.PhoneExt" class="form-control">
<div class="btnGroup @(Model.EditMode ? "visiblyHidden" : "")">
<input asp-page-handler="Edit" formnovalidate type="submit" name="btnUpdate" value="Edit" id="btnUpdate" title="Edit">
</div>
<div class="btnGroup @(Model.EditMode ? "" : "visiblyHidden")">
<input asp-page-handler="Save" type="submit" name="btnSave" value="Save" id="btnSave" title="Save">
<input asp-page-handler="Cancel" type="submit" name="btnCancel" value="Cancel" id="btnCancel" title="Cancel">
</div>
</form>
</div>
And here is the backend code:
[BindProperties]
public class MyInformationModel : PageModel
{
protected ILogger<MyInformationModel> _logger;
private readonly UserManager<T_User> _userManager;
protected ActivityLogger _activityLogger;
protected MyInformationService _myInfomationSettingsService;
public UserViewModel? MyInformation { get; set; }
public bool EditMode { get; set; } = false;
public MyInformationModel(UserManager<T_User> userManager, ILogger<MyInformationModel> logger, ActivityLogger activityLogger, MyInformationService myInformationService)
{
_logger = logger;
_activityLogger = activityLogger;
_userManager = userManager;
_myInfomationSettingsService = myInformationService;
}
public async Task OnGetAsync()
{
var user = HttpContext.User;
var userId = _userManager.GetUserId(user);
MyInformation = await _myInfomationSettingsService.GetSettingsAsync(userId);
}
public async Task<IActionResult> OnPostSave()
{
var user = await _userManager.FindByNameAsync(HttpContext?.User?.Identity?.Name?.ToString());
var userId = await _userManager.GetUserIdAsync(user);
if (ModelState.IsValid)
{
var currentPage = HttpContext?.Request.Path.ToString();
int startindex = currentPage.LastIndexOf('/') + 1;
int endindex = currentPage.Length;
currentPage = currentPage.Substring(startindex, endindex - startindex);
_logger.LogInformation($"{user} updated user information", DateTime.UtcNow.ToLongTimeString());
await _activityLogger.Log($"{user} updated user information", currentPage, userId);
await _myInfomationSettingsService.SetSettingsAsync(MyInformation);
}
else
{
await _myInfomationSettingsService.GetSettingsAsync(userId);
}
return RedirectToPage("/Refunds/MyInformation");
}
public async Task<IActionResult> OnPostCancel()
{
EditMode = false;
return RedirectToPage("/Refunds/MyInformation");
}
public async Task OnPostEdit()
{
EditMode = true;
var user = await _userManager.FindByNameAsync(HttpContext?.User?.Identity?.Name?.ToString());
var userId = await _userManager.GetUserIdAsync(user);
MyInformation = await _myInfomationSettingsService.GetSettingsAsync(userId);
TryValidateModel(MyInformation.LastName);
//ModelState.ClearValidationState("MyInformation.FirstName");
//ModelState.MarkFieldValid("MyInformation.FirstName");
//ModelState.ClearValidationState("MyInformation.LastName");
//ModelState.MarkFieldValid("MyInformation.LastName");
//ModelState.ClearValidationState("MyInformation.PhoneNumber");
//ModelState.MarkFieldValid("MyInformation.PhoneNumber");
}
}
Because all of your inputs value are not posted to backend so that the ModelState does not contain any value.
For why the backend cannot receive value is due to Browsers does not support disabled property to submit with forms by default:
The difference between disabled and readonly is that read-only controls can still function and are still focusable, whereas disabled controls can not receive focus and are not submitted with the form and generally do not function as controls until they are enabled.
For your scenario, you can change disabled
to readonly
and modify the ternary operator like below:
<input readonly="@(Model.EditMode ? null : "readonly" )" asp-for="MyInformation.FirstName" value="@MyInformation.Index.FirstName" class="form-control" />