I'm developing a web app with APS.NET Core MVC (.NET 5.0). I have an entity (Service) that have a dynamic list of another entity (OpeningHours). So a service can have different opening hours, for example:
- From 08:00 to 20:00
- From 08:00 to 13:00 and from 17:00 to 20:00
You can set different time slots, as many as you want. I didn't know how to implement this case and looking for the solution I found How to dynamically add items from different entities to lists in ASP.NET Core MVC and followed the answer adapting it to my entities. Simplifying a bit, this would be the code:
Models (or ViewModels):
public class Service
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Required]
public string Description { get; set; }
public List<ServiceOpeningHours> OpeningHours { get; set; } = new List<ServiceOpeningHours>();
}
public class ServiceOpeningHours
{
public TimeSpan From { get; set; }
public TimeSpan To { get; set; }
}
Create.cshtml (View):
@model MyWebApp.Services.Models.Service
...
<form asp-action="Create" name="createForm" id="createForm">
<div asp-validation-summary="ModelOnly" ></div>
<div >
<label >Name</label>
<input asp-for="Name" />
<span asp-validation-for="Name" ></span>
</div>
<div >
<label >Description</label>
<input asp-for="Description" />
<span asp-validation-for="Description" ></span>
</div>
<fieldset>
<legend>Opening Hours</legend>
<div id="openingHoursContainer">
@foreach (ServiceOpeningHours item in Model.OpeningHours)
{
<partial name="_OpeningHourEditor" manifest model="item" />
}
</div>
</fieldset>
<div>
<div >
<input id="addOpeningHourItem" type="button" value="Add Opening Hour" />
</div>
<div >
<input type="submit" id="submit" value="Create" />
</div>
</div>
</form>
...
@section Scripts {
$('#addOpeningHourItem').click(function (event) {
event.preventDefault();
$.ajax({
async: true,
data: $('#form').serialize(),
type: 'POST',
url: '/Services/AddBlankOpeningHour',
success: function (partialView) {
$('#openingHoursContainer').append(partialView);
}
});
});
$('#submit').click(function (event) {
event.preventDefault();
var formData = new FormData();
formData.append("Name", $('input[name="Name"]').val());
formData.append("Description", $('input[name="Description"]').val());
$("input[name='From']").each(function (i) {
var from = $(this).val();
formData.append("OpeningHours[" i "].From", from);
});
$("input[name='To']").each(function (i) {
var to = $(this).val();
formData.append("OpeningHours[" i "].To", to);
});
formData.append("__RequestVerificationToken", $('form[name="createForm"] input[name="__RequestVerificationToken"]').val());
$.ajax({
method: 'POST',
url: '/Services/Create',
data: formData,
processData: false,
contentType: false,
success: function (returnValue) {
console.log('Success: ' returnValue);
}
});
});
}
_OpeningHourEditor.cshtml (partial view for opening hour item):
@model MyWebApp.Models.ServiceOpeningHours
<div >
<div >
<div >
<label >From</label>
<input asp-for="From" />
<span asp-validation-for="From" ></span>
</div>
</div>
<div >
<div >
<label >To</label>
<input asp-for="To" />
<span asp-validation-for="To" ></span>
</div>
</div>
</div>
In the javascript code a FormData
is created and filled with all fields and the model successfully arrives at the Create
action.
ServiceController:
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Create(Service service)
{
if (ModelState.IsValid)
{
// Save data in data base...
return this.RedirectToAction("Index");
}
return View(service);
}
[HttpPost]
public ActionResult AddBlankOpeningHour()
{
return PartialView("_OpeningHourEditor", new ServiceOpeningHours());
}
With this code, when the Create
action returns the response, it reaches the success
block of $.ajax()
, but this does not cause the page to reload with the new data.
I think you shouldn't do it with AJAX. How should I make the call so that everything works normally? That is, if the Name
or Description
fields are not filled in, the ModelState
error message should be displayed, and if all goes well it should be redirected to the Index
action.
CodePudding user response:
Modified the submit script to remove ajax and initially change the name of the input fields so that the list will be bound to the model properly.
$('#submit').click(function (event) {
// initially prevent form submit
event.preventDefault();
// loop through all input with name From, and change their name with index
$("input[name='From']").each(function (i) {
$(this).attr("name", "OpeningHours[" i "].From");
});
// loop through all input with name To, and change their name with index
$("input[name='To']").each(function (i) {
$(this).attr("name", "OpeningHours[" i "].To");
});
// submit the form
$("#createForm").submit();
});