How can I bind complex Lists in ASP.NET Core RazorPages

13,131

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.

Share:
13,131
Jonas
Author by

Jonas

Updated on July 20, 2022

Comments

  • Jonas
    Jonas almost 2 years

    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.