I have a class QuestionnaireDetailsViewModel who contains some properties and also a list.
The list could contains from 0 to many items. I send the class QuestionnaireDetailsViewModel to my View in order to display it in a form. For the list part, I use a foreach to render it and it works just fine.
The problem is, all those properties are inputs and when I submit the form, my List is always empty when it's reaching the Controller.
I also noticed that in the payload of the call, it only send the first item in the list to the controller, but I believe it's because each item have the same name.
I don't understand how could I make match List properties name with each input of my form since they need to have a unique name. Is it possible to achieve this or should I save each list item separately with a JS loop?
Could you please enlighten me?
QuestionnaireDetailsViewModel class
public class QuestionnaireDetailsViewModel
{
public string Id { get; set; }
public string Name { get; set; }
public List<VenueViewModel> VenueItems { get; set; }
}
VenueViewModel class
public class VenueViewModel
{
public string Id { get; set; }
public string Id_Que { get; set; }
public string VenueName { get; set; }
public string Checkbox_Other { get; set; }
public string IsEventHaveSustainableApproach { get; set; }
}
View
<form method="post" enctype="multipart/form-data" class="form-content-column" id="formFull">
<div class="input-box">
<label class="numbered" for="Name">Name<span class="span-mandatory">*</span></label>
<input asp-for="Name" class="input-mandatory" type="text" id="Name" required />
<span asp-validation-for="Name"></span>
<input asp-for="Id" type="hidden" id="Id" required />
</div>
<div class="accordion" id="accordionExample">
@foreach (var obj in Model.VenueItems)
{
<div class="accordion-item">
<h2 class="accordion-header" id="headingOne@obj.Id">
<button class="accordion-button accordion-button-custom" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOne_@obj.Id" aria-expanded="true" aria-controls="collapseOne_@obj.Id" style="margin-top: 0px">
@obj.VenueName
</button>
</h2>
<div id="collapseOne_@obj.Id" class="accordion-collapse collapse show" aria-labelledby="headingOne_@obj.Id" data-bs-parent="#accordionExample">
<div class="accordion-body">
<div class="input-box no-border">
<label class="numbered">Did the venue have a sustainable approach?<span class="span-mandatory">*</span></label>
<div class="flex flex-column">
<div class="single-radio">
<input asp-for="@obj.IsEventHaveSustainableApproach" class="input-mandatory radio_Tab1_IsEventHaveSustainableApproach" type="radio" value="1" id="Tab1_IsEventHaveSustainableApproach_Yes_@obj.Id" />
<label for="Tab1_IsEventHaveSustainableApproach_Yes_@obj.Id">Yes</label>
</div>
<div class="single-radio">
<input asp-for="@obj.IsEventHaveSustainableApproach" class="input-mandatory radio_Tab1_IsEventHaveSustainableApproach" type="radio" value="0" id="Tab1_IsEventHaveSustainableApproach_No_@obj.Id" />
<label for="Tab1_IsEventHaveSustainableApproach_No_@obj.Id">No</label>
</div>
</div>
</div>
<div class="input-box">
<label for="Tab1_Checkbox_EventVenue_Other">Other? Please explain</label>
<input asp-for="@obj.Checkbox_Other" type="text" id="Tab1_Checkbox_EventVenue_Other" />
</div>
</div>
</div>
</div>
}
</div>
</form>
Js method to send the form
async function saveForm() {
let formData = new FormData(document.forms[0]);
await fetch('/Reports/Save', {
method: 'post',
body: formData
})
.then((response) => {
toastr.success('Saved successfully');
preventLeaving = false;
return true;
})
.catch((error) => {
toastr.error('An error occured');
return false;
});
}
Controller who receive the form back with the property Name filled but list items are empty
public async Task<IActionResult> SaveAsync(QuestionnaireDetailsViewModel questionnaireViewModel)
{
}
