1
votes

I am struggeling with data binding and Razor pages

I was not able to find a solution. Propably I am asking not the right questions. So I hope you can help me out to point me to the right direction.

I want a page to create bookings to collect all necessary data to post it to a web api when everything is done.

On that page I want to collect necessary data for the wooking and want to add x Booking Details to the Booking.

Datamodel

public class Booking
{
   [Key]
   public int Id { get; set; }

   public string Reference { get; set; }

   public List<BookingDetail> BookingDetails { get; set; }
}
public class BookingDetail
{
   [Key]
   public int Id { get; set; }

   public int Pieces { get; set; }

   public string Marks { get; set; }
}

What I would like to to is to create a create page for a booking. So far I followed this instructions, which is working Link

This missing part is that I would like to add new Booking Details as well on the same Razor page.

So I want to see a list of all added booking Details and I want to have textboxes to add new booking Details.

My idea was to do the following, but I am receiving a System.NullReferenceException as Booking is null:

@page
@model Onlinebooking.Web.Pages.BookingModel
@{
    ViewData["Title"] = "Booking";
}

<h1>Booking4</h1>
<form method="post">
    Reference: <Input asp-for="Booking.Reference" /><br />
    Pieces: <input asp-for="Booking.BookingDetails[0].Pieces" /><br />
    Marks: <input asp-for="Booking.BookingDetails[0].Marks" /><br />
    <br />
    <input type="submit" value="submit" />
</form>

<form method="post" asp-page-handler="DeleteBookingDetails">
    @for (var i = 0; i < @Model.Booking.BookingDetails.Count(); i++)
    {
        @Html.LabelFor(x => x.Booking.BookingDetails[i].Pieces)
        @Html.LabelFor(x => x.Booking.BookingDetails[i].Marks)
    }
</form>

Is it even possible to do all that on one page or should I go in a different direction?

Thanks for your help!

2
This is possible; you can even use templates to define a proper EditorFor ... btw, why the -1 in @Model.Booking.BookingDetails.Count()-1?Stefan
It's probably hard because you are trying to use your data model as a view model. They really should be different things (with probably many of the same properties). Everything you see on the UI/View should have a property in the ViewModel, but not all of those properties will end up in your database.Neil
@Stefan I see that the -1 is a mistake and makes no sense. I edited the post.Matze Donien

2 Answers

0
votes

There are a couple concepts I have had much difficulty with also in your question.

The first issue you are coming across is in the CSHTML.CS file. Initializing your variables will solve that problem.

public class BookingModel : PageModel
{
    private readonly DataDbContext _context;

    public BookingModel(DataDbContext context)
    {
        _context = context;
    }

    public List<Booking> Bookings { get; set; } = new List<Booking>();
    public Booking Booking { get; set; } = new Booking();
    public BookingDetail BookingDetail { get; set; } = new BookingDetail();

    public void OnGet()
    {
        Bookings = _context.Bookings.Include(x => x.BookingDetails).ToList();
    }
}

And on the CSHTML side I would slightly modify how you are referencing the labels and printing your FOR loops.

<form method="post" asp-page-handler="DeleteBookingDetails">
<table>
    <thead>
        <tr>
            <th><label asp-for="BookingDetail.Pieces" class="control-label"></label></th>
            <th><label asp-for="BookingDetail.Marks" class="control-label"></label></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var booking in Model.Bookings)
        {
            foreach (var bookingDetail in booking.BookingDetails)
            {
                <tr>
                    <td>@bookingDetail.Pieces</td>
                    <td>@bookingDetail.Marks</td>
                </tr>
            }
        }
    </tbody>
</table>

I have not modified Booking or BookingDetail.

I believe you will be able to figure out the rest of your functionality from here.

0
votes

Here is a demo to add and delete BookingDetai:

cshtml:

<form method="post">
    <Input asp-for="Booking.Id" hidden />
    Reference: <Input asp-for="Booking.Reference" /><br />
    Pieces: <input name="Booking.BookingDetails[@Model.Booking.BookingDetails.Count()].Pieces" /><br />
    Marks: <input name="Booking.BookingDetails[@Model.Booking.BookingDetails.Count()].Marks" /><br />
    <br />
    <input type="submit" value="submit" asp-page-handler="AddBookingDetails"/>
    <div hidden>
        @for (var i = 0; i < @Model.Booking.BookingDetails.Count(); i++)
        {
            <input asp-for="@Model.Booking.BookingDetails[i].Id" hidden />

            <input asp-for="@Model.Booking.BookingDetails[i].Pieces" hidden />

            <input asp-for="@Model.Booking.BookingDetails[i].Marks" hidden />


        }
    </div>
   
</form>

    <table>
        <thead>
            <tr>
                <th><label class="control-label">Pieces</label></th>
                <th><label class="control-label">Marks</label></th>
                <th><label class="control-label">Action</label></th>
            </tr>
        </thead>
        <tbody>
            @for (var i = 0; i < @Model.Booking.BookingDetails.Count(); i++)
            {
            <tr>
                <form method="post" >
                    <div hidden>
                        @for (var j = 0; j < @Model.Booking.BookingDetails.Count(); j++)
                        {
                            <input asp-for="@Model.Booking.BookingDetails[j].Id" hidden />

                            <input asp-for="@Model.Booking.BookingDetails[j].Pieces" hidden />

                            <input asp-for="@Model.Booking.BookingDetails[j].Marks" hidden />


                        }
                    </div>
                    <td>@Model.Booking.BookingDetails[i].Pieces</td>
                    <input name="Booking.Id" hidden value="@Model.Booking.Id" />
                    <input name="Booking.Reference" hidden value="@Model.Booking.Reference" />
                    <input name="BookingDetail.Id" hidden value="@Model.Booking.BookingDetails[i].Id" />
                    <td>@Model.Booking.BookingDetails[i].Marks</td>
                    <td><button asp-page-handler="DeleteBookingDetails">delete</button></td>
                </form>
            </tr>
            }
        </tbody>
    </table>

cshtml.cs(I use simple data to test it):

   public class CreateBookingModel : PageModel
    {
        [BindProperty]
        public Booking Booking { get; set; }
        [BindProperty]
        //BookingDetail is used to delete BookingDetail 
        public BookingDetail BookingDetail { get; set; }
        public static int count;
        public ActionResult OnGet()
        {
            List<BookingDetail> list = new List<BookingDetail> { new BookingDetail { Id = 1, Pieces = 2, Marks = "m1" }, new BookingDetail { Id = 2, Pieces = 1, Marks = "m2" } };
            Booking = new Booking { Id = 1, Reference = "r", BookingDetails = list };
            count = 2;
            return Page();
        }
        public ActionResult OnPostAddBookingDetails() {

            Booking.BookingDetails[Booking.BookingDetails.Count()-1].Id = ++count;

            return Page();
        }
        public ActionResult OnPostDeleteBookingDetails() {
            Booking.BookingDetails.RemoveAll(b => b.Id == BookingDetail.Id);
            return Page();
        }
    }

result: enter image description here