#ajax #asp.net-core #asp.net-core-mvc #partial-views #modelstate
#ajax #asp.net-ядро #asp.net-core-mvc #частичные представления #modelstate
Вопрос:
На моей главной странице есть частичное представление, которое загружает форму, модель ниже:
public class CreateRequestViewModel
{
[Required]
public short ClientId { get; set; }
[Required]
public Guid SystemId { get; set; }
[Required]
public string RequestedUsername { get; set; }
public string TicketReference { get; set; }
public string Notes { get; set; }
public List<SelectListItem> Clients { get; set; }
public List<SelectListItem> Systems { get; set; }
}
Это частичное представление:
@model Models.CreateRequestViewModel
@Html.AntiForgeryToken()
@Html.ValidationSummary(true, "", new { @class = "text-danger" })
<div class="row">
<div class="col-lg-4 col-md-4 col-sm-12">
<h1>Create a Request</h1>
</div>
<div class="col-lg-8 col-md-8 col-sm-12 right">
<div class="form-group">
@Html.DropDownListFor(m => m.ClientId, Model.Clients, htmlAttributes: new { @class = "form-control form-control-lg", @id = "ClientSelect" })
@Html.ValidationMessageFor(m => m.ClientId, "", htmlAttributes: new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.DropDownListFor(m => m.SystemId, Model.Systems, htmlAttributes: new { @class = "form-control form-control-lg", @id = "ClientSystemSelect" })
@Html.ValidationMessageFor(m => m.SystemId, "", htmlAttributes: new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.TextBoxFor(m => m.RequestedUsername, htmlAttributes: new { @class = "form-control form-control-lg", @placeholder = "Username" })
@Html.ValidationMessageFor(m => m.RequestedUsername, "", htmlAttributes: new { @class = "text-danger" })
</div>
<div class="form-group">
@Html.TextBoxFor(m => m.TicketReference, htmlAttributes: new { @class = "form-control form-control-lg", @placeholder = "Ticket reference" })
</div>
<div class="form-group">
@Html.TextAreaFor(m => m.Notes, htmlAttributes: new { @class = "form-control form-control-lg", @rows = 3, @placeholder = "Notes..." })
</div>
<input type="Submit" class="btn btn-secondary btn-block send-request" value="Submit" name="">
</div>
</div>
Вот как я загружаю страницу:
<div class="container">
<div class="row">
<div class="col-lg-6">
<form asp-action="CreateRequest" asp-controller="Access"
data-ajax="true"
data-ajax-method="POST"
data-ajax-mode="replace"
data-ajax-update="#createRequest">
<div id="createRequest">
@await Html.PartialAsync("_CreateRequest", Model.CreateRequestModel)
</div>
</form>
</div>
</div>
</div>
При использовании модели в том виде, в каком она есть, и с использованием ненавязчивого javascript, например, оставление RequestedUsername пустым приведет к тому, что форма не будет отправлена и для нее появится сообщение о проверке. Это здорово.
Однако у меня есть требование сначала проверить данные формы на соответствие записям в базе данных и выдать ошибку, если существует существующая запись. Я подумал, что после прохождения всей проверки на стороне клиента я бы использовал ModelState.Добавьте modelerror в контроллер вот так:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CreateRequest(CreateRequestViewModel model)
{
if(model.RequestedUsername == "someincorrectvalue"){ //actual logic removed for brevity
ModelState.AddModelError("RequestedUsername", "Already in use");
}
if(!ModelState.IsValid)
{
//reset lists on model, removed
return PartialView("_CreateRequest", model);
}
_logger.LogInformation("CreateRequest successful");
return RedirectToAction(nameof(Index));
}
Однако, если я использую ModelState.AddModelError, return PartialView("_CreateRequest", model)
вызов завершается перезагрузкой всей страницы, как если бы она возвращала полный вид.
Я в недоумении, почему это происходит. Разница, которую я вижу, заключается в том, что я добавляю ошибку ModelState внутри контроллера, тогда как в противном случае проверка выполняется на стороне клиента.
У кого-нибудь есть идея?
Ответ №1:
Итак, это оказалось сочетанием проблем. Начнем с того, что ненавязчивые Ajax-скрипты, которые были у меня в моем решении, не работали. Я не знаю почему, но я заменил их одним из CDN: https://ajax.aspnetcdn.com/ajax/jquery.unobtrusive-ajax/3.2.5/jquery.unobtrusive-ajax.min.js
Это решило проблему перезагрузки всей страницы вместо частичного возврата через ненавязчивый ajax.
Вторая проблема заключалась в том, что в случае успеха я перенаправлял на действие контроллера индекса вместо того, чтобы снова возвращать частичное. Это приводило к отображению всей индексной страницы внутри div, который я выбрал в качестве своей цели ajax. Мое действие контроллера теперь выглядит так:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult CreateRequest(CreateRequestViewModel model)
{
if(model.RequestedUsername == "someincorrectvalue"){ //actual logic removed for brevity
ModelState.AddModelError("RequestedUsername", "Already in use");
}
if(!ModelState.IsValid)
{
//reset lists on model, removed
return PartialView("_CreateRequest", model);
}
_logger.LogInformation("CreateRequest successful");
// reset lists on model, removed
ModelState.Clear(); // get rid ofany model details to make way for a new request
return PartialView("_CreateRequest", model);
}