I'm using the following script to dynamically add and delete fields to a card within a form. I'm trying to pass the values to my model binding property, which is a List<ushort>
<div style="width:40%;">
<div >
<div >
<div id="row">
<div >
<div >
<button
id="DeleteRow"
type="button">
<i ></i>
Delete
</button>
</div>
<input type="text" >
</div>
</div>
<div id="newinput"></div>
<button id="rowAdder" type="button" >
<span >
</span> ADD
</button>
</div>
</div>
</div>
<script type="text/javascript">
$("#rowAdder").click(function () {
newRowAdd =
'<div id="row"> <div >'
'<div >'
'<button id="DeleteRow" type="button">'
'<i ></i> Delete</button> </div>'
'<input type="text" > </div> </div>';
$('#newinput').append(newRowAdd);
});
$("body").on("click", "#DeleteRow", function () {
$(this).parents("#row").remove();
})
</script>
I've tried using a hidden field bound to the model property and updating the field on submit to an array.
function updatePortsInput() {
console.log("updatePortsInput called");
var ports = [];
$(".m-input").each(function () {
var portValue = parseInt($(this).val());
if (!isNaN(portValue) && portValue >= 0 && portValue <= 65535) {
ports.push(portValue);
}
});
$('[name="Ports"]').val(JSON.stringify(ports));
}
<input type="hidden" id="portsInput" name="Ports" asp-for="Ports" />
But I'm unable to populate my List with the values of the generated fields.
Does someone have an idea on this?
CodePudding user response:
You’re mixing form and json content in your request. You’re not showing how exactly you send the request, but it looks like you’re submitting a form. By default this will send content of type application/x-www-form-urlencoded
, so the request body will look like this (formatted):
ports: "[1,2,3,4,5,6,7]"
__RequestVerificationToken: "CfDJ8KVrOUsD…"
The framework will now match your handler parameter ports
to the value in the request, which is a string. It doesn’t know that the string is a json array, so it will fail to parse it into the specified type List<ushort>
.
You have to decide:
- Keep sending mixed content and parse the Json on the backend manually
- Send a Form request body
- Send a Json request body
Option 1: Parse Json yourself
Keep everything the way it is, but on your page handler, change List<ushort> ports
to string ports
and then call JsonSerializer.Deserialize<List<ushort>>(ports)
on it.
This may be interesting if you have other inputs you want to send with the same request, and you want the framework to handle them the usual way. But really, I’d prefer Option 2.
Option 2: Send Form content
Give all your inputs the name ports
, remove the Json stuff, submit the form, it works.
<form asp-page-handler="Form" method="POST">
<input name="ports" type="number" value="1"/>
<input name="ports" type="number" value="2"/>
<input name="ports" type="number" value="3"/>
<button type="submit">Submit</button>
</form>
public IActionResult OnPostForm(List<ushort> ports)
{
foreach (var port in ports)
Console.WriteLine(port);
return RedirectToPage();
}
Just do this one.
Option 3: Send Json content
Don’t put your Json array in a hidden form input. Instead, put it into the request body and declare the request content type as application/json
. Tell the page handler to get the parameter from the request body. The framework will magically parse the json for you:
<form asp-page-handler="Json" onsubmit="go(event)">
<input type="number" value="1" />
<input type="number" value="2" />
<input type="number" value="3" />
<button type="submit">Submit</button>
</form>
<script>
$(".m-input").each(function () {
var portValue = parseInt($(this).val());
if (!isNaN(portValue) && portValue >= 0 && portValue <= 65535) {
ports.push(portValue);
}
});
//above is the code that collects your inputs into an array,
//which i copied from you.
//below is a vanilla javascript fetch request that sends this
//array as json. You can probably do that with jQuery, too,
//but I don’t know how.
async function go(e) {
e.preventDefault();
fetch('@Url.Page("", "Json")', {
method: 'POST',
headers: new Headers({ 'content-type': 'application/json' }),
body: JSON.stringify(ports),
});
}
</script>
public IActionResult OnPostJson([FromBody] List<ushort> ports)
{
foreach (var port in ports)
Console.WriteLine(port);
return new JsonResult("thx i enjoyed those ports");
}
In contrast to the other options, this will send an asynchronous request, i.e. the browser won’t navigate.
Because you’re building the request yourself with javascript, the CSRF token that ASP.NET Core probably generates with your form will not be sent automatically, so you need to figure that out (you can send it in a header or have your backend ignore it).