Project is an ASP.NET Core 6 webapp. I have a property that has decorated with [BindProperty]
with some object arrays inside of it. I have a page that has a form that displays all the properties of the model including the properties of the nested objects in the arrays. I have some js functions that add and remove form controls that correspond to the properties of my model bound object for the purposes of changing the amount of objects in the arrays. For example, I have a Sources
array and the client decides to add a device, they push a plus button and boom new device object appears on the page. If I then post this object back to the PageModel
the model bound property now has this new object in the device array with all the fields and comes up valid. HOWEVER, this does not work in the opposite. If the client wants to remove a device, the java-script function removes the HTML element but when it's posted, the ModelState.MyProperty.Sources
still shows the device there in the array with all of it's properties as null.
It seems like whatever the model looks like going to the client, it comes back the same way? I see the ModelState
dictionary that is generated still has entries for Config.Sources[<deletedindex>].Id
when the HTML coming back to the server doesn't have any attributes that call for it.
As a note, HttpContext.Request.Form.Keys
does NOT have entries for the removed array objects but Model.Config.Sources
does.
JS functions in case these are my problem.
const sourceSection = document.querySelector(".source-section");
const nextCard = document.querySelectorAll(".source-card").length;
const newDiv = document.createElement("div");
newDiv.classList.add("col-6");
newDiv.classList.add("p-4");
newDiv.classList.add("border");
newDiv.classList.add("source-card");
newDiv.innerHTML = `
<div class="row col">
<h3>Source ${nextCard}</h3>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Id" class="col-6 my-1">Id</label>
<input type="text" data-val="true" data-val-required="Source Id field can not be empty" id="Config_Sources_${nextCard}__Id" name="Config.Sources[${nextCard}].Id" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].Id" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Name" class="col-6 my-1">Name</label>
<input type="text" data-val="true" data-val-required="The Name field is required." id="Config_Sources_${nextCard}__Name" name="Config.Sources[${nextCard}].Name" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].Name" data-valmsg-replace="true"></span>
<div data-lastpass-icon-root="true" style="position: relative !important; height: 0px !important; width: 0px !important; float: left !important;"></div></div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Icon" class="col-6 my-1">Icon</label>
<input type="text" data-val="true" data-val-required="The Icon field is required." id="Config_Sources_0__Icon" name="Config.Sources[${nextCard}].Icon" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].Icon" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Position" class="col-6 my-1">Position</label>
<input type="text" data-val="true" data-val-required="The Position field is required." id="Config_Sources_${nextCard}__Position" name="Config.Sources[${nextCard}].Position" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].Position" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Label" class="col-6 my-1">Label</label>
<input type="text" data-val="true" data-val-required="The Label field is required." id="Config_Sources_${nextCard}__Label" name="Config.Sources[${nextCard}].Label" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].Label" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__nsMessage" class="col-6 my-1">No Signal Message</label>
<input type="text" id="Config_Sources_${nextCard}__nsMessage" name="Config.Sources[${nextCard}].nsMessage" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].nsMessage" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__nsHelpMessage" class="col-6 my-1">No Signal Help Message</label>
<input type="text" id="Config_Sources_${nextCard}__nsHelpMessage" name="Config.Sources[${nextCard}].nsHelpMessage" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].nsHelpMessage" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__pMessage" class="col-6 my-1">Presentation Message</label>
<input type="text" id="Config_Sources_${nextCard}__pMessage" name="Config.Sources[${nextCard}].pMessage" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].pMessage" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__pIcon" class="col-6 my-1">Presentation Icon</label>
<input type="text" id="Config_Sources_0__pIcon" name="Config.Sources[${nextCard}].pIcon" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].pIcon" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__nsIcon" class="col-6 my-1">No Signal Icon</label>
<input type="text" id="Config_Sources_${nextCard}__nsIcon" name="Config.Sources[${nextCard}].nsIcon" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].nsIcon" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__subpageId" class="col-6 my-1">Subpage Id</label>
<input type="text" id="Config_Sources_${nextCard}__subpageId" name="Config.Sources[${nextCard}].subpageId" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].subpageId" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__HasVideo" class="col-6 my-1">Has Video</label>
<input type="checkbox" data-val="true" data-val-required="The Has Video field is required." id="Config_Sources_${nextCard}__HasVideo" name="Config.Sources[${nextCard}].HasVideo" value="false" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__HasAudio" class="col-6 my-1">Has Audio</label>
<input type="checkbox" data-val="true" data-val-required="The Has Audio field is required." id="Config_Sources_${nextCard}__HasAudio" name="Config.Sources[${nextCard}].HasAudio" value="false" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__VideoRequired" class="col-6 my-1">Video Req.</label>
<input type="checkbox" data-val="true" data-val-required="The Video Req. field is required." id="Config_Sources_${nextCard}__VideoRequired" name="Config.Sources[${nextCard}].VideoRequired" value="false" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_0__BypassP" class="col-6 my-1">Bypass Presentation</label>
<input type="checkbox" data-val="true" data-val-required="The Bypass Presentation field is required." id="Config_Sources_${nextCard}__BypassP" name="Config.Sources[${nextCard}].BypassP" value="false" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_0__Disabled" class="col-6 my-1">Disabled</label>
<input type="checkbox" data-val="true" data-val-required="The Disabled field is required." id="Config_Sources_${nextCard}__Disabled" name="Config.Sources[${nextCard}].Disabled" value="false" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Pinned" class="col-6 my-1">Pinned</label>
<input type="checkbox" data-val="true" data-val-required="The Pinned field is required." id="Config_Sources_${nextCard}__Pinned" name="Config.Sources[${nextCard}].Pinned" value="false" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__DefaultMode" class="col-6 my-1">Default Mode</label>
<input type="text" data-val="true" data-val-required="The Default Mode field is required." id="Config_Sources_${nextCard}__DefaultMode" name="Config.Sources[${nextCard}].DefaultMode" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[0].DefaultMode" data-valmsg-replace="true"></span>
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__DetectionId" class="col-6 my-1">Detection Id</label>
<input type="text" data-val="true" data-val-required="The Detection Id field is required." id="Config_Sources_${nextCard}__DetectionId" name="Config.Sources[${nextCard}].DetectionId" value="" class="col-6 my-1">
<span class="field-validation-valid" data-valmsg-for="Config.Sources[${nextCard}].DetectionId" data-valmsg-replace="true"></span>
</div>
<h4>Route 0</h4>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Routes_0__Id" class="col-6 my-1">Id</label>
<input type="text" data-val="true" data-val-required="The Id field is required." id="Config_Sources_${nextCard}__Routes_0__Id" name="Config.Sources[${nextCard}].Routes[0].Id" value="" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Routes_0__Input" class="col-6 my-1">Input</label>
<input type="text" id="Config_Sources_${nextCard}__Routes_0__Input" name="Config.Sources[${nextCard}].Routes[0].Input" value="" class="col-6 my-1">
</div>
<div class="row seperator">
<label for="Config_Sources_${nextCard}__Routes_0__Output" class="col-6 my-1">Output</label>
<input type="text" id="Config_Sources_${nextCard}__Routes_0__Output" name="Config.Sources[${nextCard}].Routes[0].Output" value="" class="col-6 my-1">
</div>
`;
sourceSection.appendChild(newDiv);
};
function removeSource() {
const sourceSection = document.querySelector(".source-section");
const sourceCards = document.querySelectorAll(".source-card");
const target = sourceCards[sourceCards.length - 1];
sourceSection.removeChild(target);
};```