Home > Blockchain >  Submit changed FormData in ASP.NET form to have normal behavior with ModelState and redirections
Submit changed FormData in ASP.NET form to have normal behavior with ModelState and redirections

Time:03-01

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();
});
  • Related