Search code examples
c#asp.net-core.net-coreentity-framework-corerazor-pages

Form won't save value from drop down list (Razor pages)


I'm currently working on a form where you can create a new car which gets saved into a list of cars in my database. However, when you create a car, you can also select a brand from a drop down list. I've inserted the values of the options from a table/model which is called Brands. Right now, you can choose a brand, however, when I click submit everything other than the brand gets saved.

*I added the first row manually in SQL Server, that's why the first car has a brand assigned to it

Is it that I've written the wrong thing in the value attribute of the option tag or does the issue lie in something else?

Create.cshtml.cs

namespace CarRental.Pages.CarsList
{
    public class CreateModel : PageModel
    {
        private readonly ApplicationDbContext _db;

        public CreateModel(ApplicationDbContext db)
        {
            _db = db;
        }

        [BindProperty]
        public Cars Car { get; set; }

        public IList<Brands> Brands { get; set; }

        public void OnGet()
        {
            // going inside the DB, retrieving all the brands and storing them in the IList
            Brands = _db.Brands.ToList();
        }

        public async Task<IActionResult> OnPost(Cars carObj)
        {
            if (ModelState.IsValid)
            {
                //add item in a queue so that it can be pushed to the database
                _db.Cars.AddAsync(Car);
                //save changes to the database, aka push changes to the database
                await _db.SaveChangesAsync();

                //once the changes are pushed to the database
                return RedirectToPage("Index");
            } 
            else
            {
                return Page();
            }
        }
    }
}

Create.cshtml

@page
@model CarRental.Pages.CarsList.CreateModel
@{
    ViewData["Title"] = "Create";
}

<h2 class="text-info">Create New Book</h2>
<br />

<div class="border container" style="padding:30px;">
    <form method="post">
        <div class="text-danger" asp-validation-summary="ModelOnly"></div>
        <div class="form-group row">
            <div class="col-3">
                <label asp-for="Car.LicensePlate"></label>
            </div>
            <div class="col-6">
                <input asp-for="Car.LicensePlate" class="form-control" />
            </div>

        </div>
        <div class="form-group row">
            <div class="col-3">
                <label asp-for="Car.Brand"></label>
            </div>
            <div class="col-6">
                <select asp-for="Car.Brand" class="form-control">
                  
                    @foreach (var item in Model.Brands)
                    {
                    <option value=">@Html.DisplayFor(m => item.Brand)">@Html.DisplayFor(m => item.Brand)</option>
                    }
                   
                </select>
            </div>

        </div>
        <div class="form-group row">
            <div class="col-3">
                <label asp-for="Car.Price"></label>
            </div>
            <div class="col-6">
                <input asp-for="Car.Price" class="form-control" />
            </div>

        </div>

        <div class="form-group row">
            <div class="col-3 offset-3">
                <input type="submit" value="Create" class="btn btn-primary form-control" />
            </div>
            <div class="col-3">
                <a asp-page="Index" class="btn btn-success form-control">Back to List</a>
            </div>
        </div>
    </form>
    @Html.ValidationSummary();
</div>

Brands.cs

namespace CarRental.Models
{
    public class Brands
    {
        [Key]
        public string Brand { get; set; }
    }
}

My Cars.cs model, in case that's of interest

namespace CarRental.Models
{
    public class Cars
    {
        [Key]
        public string LicensePlate { get; set; }
        public Brands Brand { get; set; }
        [Required]
        public float Price { get; set; }
        [Required]
        public bool StatusRented { get; set; } = true;
        
        public DateTime? StartDate { get; set; }
    }
}

Thank you in advance!


Solution

  • As far as I know, if we want to use asp.net core model binding to bind the complex types. You should make sure the Prefix = parameter name.

    Model binding starts by looking through the sources for the key Instructor.ID. If that isn't found, it looks for ID without a prefix.

    Like below this:

    You have set asp-for="Car.LicensePlate", that means it will send the data like this:

    enter image description here

    Since your post method's parameter name is carObj, so the modelbinding couldn't find the right property name.

    You should modify the parameter name to Car and then it will work well.

    Besides, I suggest you should modify the <select asp-for="Car.Brand" class="form-control"> to <select asp-for="Car.Brand.Brand" class="form-control"> then asp.net core will bind the Brand in the car.

    Also I found you set the <option value=">@Html.DisplayFor(m => item.Brand)"> , this also doesn't generate the right value. It will generate the value as >Microsoft.AspNetCore.Mvc.ViewFeatures.StringHtmlContent instead of the right item brand value. I suggest you could directly use @item.Brand and then it will work well.

    Image:

    enter image description here

    More details, you could refer to below example:

    <div class="border container" style="padding:30px;">
        <form method="post">
            <div class="text-danger" asp-validation-summary="ModelOnly"></div>
            <div class="form-group row">
                <div class="col-3">
                    <label asp-for="Car.LicensePlate"></label>
                </div>
                <div class="col-6">
                    <input asp-for="Car.LicensePlate" class="form-control" />
                </div>
    
            </div>
            <div class="form-group row">
                <div class="col-3">
                    <label asp-for="Car.Brand"></label>
                </div>
                <div class="col-6">
                    <select asp-for="Car.Brand.Brand" class="form-control">
    
                        @foreach (var item in Model.Brands)
                        {
                            <option value=">@Html.DisplayFor(m => item.Brand)">@Html.DisplayFor(m => item.Brand)</option>
                        }
    
                    </select>
                </div>
    
            </div>
            <div class="form-group row">
                <div class="col-3">
                    <label asp-for="Car.Price"></label>
                </div>
                <div class="col-6">
                    <input asp-for="Car.Price" class="form-control" />
                </div>
    
            </div>
    
            <div class="form-group row">
                <div class="col-3 offset-3">
                    <input type="submit" value="Create" class="btn btn-primary form-control" />
                </div>
                <div class="col-3">
                    <a asp-page="Index" class="btn btn-success form-control">Back to List</a>
                </div>
            </div>
        </form>
        @Html.ValidationSummary();
    </div> 
    

    Code:

        [BindProperty]
        public Cars Car { get; set; }
    
        public IList<Brands> Brands { get; set; }
    
    
        public void OnGet()
        {
            Brands = new List<Brands> { new Brands { Brand="teta" }, new Brands { Brand="tetb" } };
    
        }
    
        public async Task<IActionResult> OnPost(Cars Car) {
            return Page();
        }
    

    Result:

    enter image description here