I'm new to ASP.NET Core Razor Pages. I try to retrieve a List<> from a Page via POST. If I bind primitive Data types, I didn't face any problems. However, If I want to pass data from my Page to the Server, which contains a List I got trouble. I was able to pass the data from the server to the client, but not back.
This is an extract from my Code:
The RazorPage:
<form method="post">
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Positions[0].Number)
</th>
<th>
@Html.DisplayNameFor(model => model.Positions[0].IsSelected)
</th>
</tr>
</thead>
<tbody>
@if (!(Model.Positions is null))
{
@foreach (var item in Model.Positions)
{
<tr>
<td>
@Html.DisplayFor(modelItem => item.Number)
</td>
<td>
@Html.CheckBoxFor(modelItem => item.IsSelected)
</td>
</tr>
}
}
</tbody>
</table>
<input type="submit" />
The Backend C#-file:
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Project.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
namespace Project.Pages
{
public class TestModel : PageModel
{
private readonly DBContext _context;
[FromRoute]
public int? Id { get; set; }
public List<SelectListItem> CustomerOrder { get; set; } = new List<SelectListItem>();
[BindProperty]
public string SelectedNumber { get; set; }
[BindProperty]
public List<Position> Positions { get; set; }
public TestModel(Project.Models.DBContext context)
{
_context = context;
}
public void OnGet()
{
if (!(Id is null))
{
_context.CustomerOrder.Select(co => co.OrderNumber).Distinct().ToList().ForEach(y => CustomerOrder.Add(new SelectListItem(text: y, value: y)));
}
}
public async Task OnPostAsync()
{
if (!(SelectedNumber is null))
{
string s = $@"
select * from Table1 xyz where xyz.Column1 in (
SELECT distinct Column1
FROM Table1
where value = '" + SelectedNumber + "') and xyz.name = 'SLLZ'";
var res = await _context.Table1.FromSql(s).Select(x => x.ValueDescription).Distinct().OrderBy(x => x).ToListAsync();
Positions = new List<Position>();
foreach (var item in res)
{
Positions.Add(new Position { Number = item });
}
}
_context.CustomerOrder.Select(co => co.OrderNumber).Distinct().ToList().ForEach(y => CustomerOrder.Add(new SelectListItem(text: y, value: y)));
}
}
public class Position
{
public string Number { get; set; }
public bool IsSelected { get; set; }
}
}
If I set a Breakpoint at the beginning of the OnPost-Method, I would expect the List to be filled with the IsSelected-Property related to the user's input in the checkbox, but it isn't.
The key to complex object binding is ensuring that a sequential index in square brackets is added to the form field's name attribute e.g [0].IsSelected
or Positions[0].IsSelected
in your case. You can output the correct HTML using a for
loop and tag helpers quite easily.
You can read more about the principal here: https://www.learnrazorpages.com/razor-pages/model-binding#binding-complex-collections. Then you should be able to apply it to your application.