Search code examples
c#asp.net-corerazor-pages

How to use BindProperty to bind complex property of an HTML select element


Suppose I have the following model

public class MyModel {
    public ProgramModel(Guid id, string name, string description, Currency currency) {
        Id = id;
        Name = name;
        Description = description;
        Currency = currency;
    }

    public Guid Id { get; set; }
    public string Name { get; set; }
    public string Description { get; set; }
    public Currency Currency { get; set; }
}

The type Currency is

public class Currency {
    public Currency(string numericCode, string code, string name) {
        NumericCode = numericCode;
        Code = code;
        Name = name;
    }

    public string NumericCode { get; set; }
    public string Code { get; set; }
    public string Name { get; set; }
}

Usage example
var currency = new Currency("978", "EUR", "Euro");

Within my Razor page model class I have

[BindProperty]
public MyModel MyModel { get; set; }

[BindProperty]
public IEnumerable<Currency> AllCurrencies { get; set; }

Thanks to this i can render a select item in the view with code similar to this one

<select class="form-select" asp-for="MyModel.Currency">
    @foreach (var currency in Model.AllCurrencies) {
        if (currency.Code == Model.MyModel.Currency.Code) {
            <option value="??????" data-numericcode="@currency.NumericCode" 
               data-code="@currency.Code" data-name="@currency.Name" selected="">
               @currency.Name</option>
        }
        else {
            <option value="??????" data-numericcode="@currency.NumericCode" 
               data-code="@currency.Code" data-name="@currency.Name">
               @currency.Name</option>
        }
    }
</select>

Is there any way to instruct the model binder to recreate the Currency object when a different option item is selected on the client?


Solution

  • Is there any way to instruct the model binder to recreate the Currency object when a different option item is selected on the client?

    You can add hidden fields in the form to pass the properties of Currency like:

            <select id="currencySelect" class="form-select" asp-for="MyModel.Currency" onchange="updateCurrencyFields()">
                @foreach (var currency in Model.AllCurrencies)
                {
                    if (currency.Code == Model.MyModel.Currency.Code)
                    {
                        <option value="??????" data-numericcode="@currency.NumericCode"
                                data-code="@currency.Code" data-name="@currency.Name" selected="">
                            @currency.Name
                        </option>
                    }
                    else
                    {
                        <option value="??????" data-numericcode="@currency.NumericCode"
                                data-code="@currency.Code" data-name="@currency.Name">
                            @currency.Name
                        </option>
                    }
                }
            </select>
            <input type="hidden" asp-for="MyModel.Currency.NumericCode" id="NumericCode" />
            <input type="hidden" asp-for="MyModel.Currency.Code" id="Code"/>
            <input type="hidden" asp-for="MyModel.Currency.Name" id="Name"/>
        </div>
        <button type="submit">Submit</button>
    </form>
    
    <script>
        function updateCurrencyFields() {
            var select = document.getElementById("currencySelect");
    
            var numericcode = select.options[select.selectedIndex].getAttribute("data-numericcode");
            var code = select.options[select.selectedIndex].getAttribute("data-code");
            var name = select.options[select.selectedIndex].getAttribute("data-name");
    
            document.getElementById("NumericCode").value = numericcode;
            document.getElementById("Code").value = code;
            document.getElementById("Name").value = name;
        }
    </script>
    

    And code in the MyCurrencyPageModel like:

    public class MyCurrencyPageModel : PageModel
    {
        private readonly ApplicationDbContext _context;
        public MyCurrencyPageModel(ApplicationDbContext context)
        {
            _context = context;
        }
    
        [BindProperty]
        public MyModel MyModel { get; set; }
    
        public IEnumerable<Currency> AllCurrencies { get; set; }
    
        public void OnGet()
        {
            AllCurrencies = _context.Currencies.ToList();
            MyModel = new MyModel(Guid.NewGuid(), "Example Program", "Description", AllCurrencies.First());
        }
    
        public IActionResult OnPost()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
    
            MyModel.Id = Guid.NewGuid(); 
    
            var item = _context.Currencies.Where(v=>v.Code ==MyModel.Currency.Code).FirstOrDefault();
            MyModel.Currency = item;
    
            _context.MyModels.Add(MyModel);
            _context.SaveChanges();
    
            return RedirectToPage("Index");
        }
    }
    

    With the test result:

    Img1

    Img2

    Img3