将选择列表绑定到ASP.NET Core中的复杂对象

问题描述 投票:0回答:1

使用ASP.NET Core Razor页面,是否可以将选择列表绑定到复杂对象而无需在PageModel中具有ID属性?

我有这些模型类:

public class Department
{
    [HiddenInput]
    public int ID { get; set; }

    [Display(Name = "Name")]
    [Required(ErrorMessage = "{0} must be specified.")]
    [StringLength(50, MinimumLength = 1, ErrorMessage = "{0} has to be {2} to {1} characters long.")]
    public string Name { get; set; }
}

public class User
{
    [HiddenInput]
    public int ID { get; set; }

    [Display(Name = "Name")]
    [Required(ErrorMessage = "{0} must be specified.")]
    [StringLength(50, MinimumLength = 1, ErrorMessage = "{0} has to be {2} to {1} characters long.")]
    public string Name { get; set; }

    [Display(Name = "Department")]
    [Required(ErrorMessage = "{0} must be specified.")]
    public Department Department { get; set; }
}

我已经创建了一个用于编辑用户的视图,包括选择部门。这是我的PageModel:

public class EditModel : PageModel
{
    private readonly IUserRepository _userRepository;

    [BindProperty(BinderType = typeof(UserModelBinder))]
    public User UserToEdit { get; set; }

    public SelectList DepartmentsSL { get; set; }

    public EditModel(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public void OnGet(int id)
    {
        UserToEdit = _userRepository.GetUser(id);
        PopulateSelectList();
    }

    public IActionResult OnPost()
    {
        if (!ModelState.IsValid)
        {
            PopulateSelectList();
            return Page();
        }

        var success = _userRepository.UpdateUser(UserToEdit);

        if (success)
        {
            return RedirectToPage("./Index");
        }
        else
        {
            throw new Exception("Could not save the user.");
        }
    }

    private void PopulateSelectList()
    {
        var departments = _userRepository.GetAllDepartments();
        DepartmentsSL = new SelectList(departments, nameof(Department.ID), nameof(Department.Name), UserToEdit.Department.ID);
    }
}

...这是我的观点:

@page
@model ProjectName.Pages.Admin.Users.EditModel
@{
    ViewData["Title"] = "Edit user";
}

<h1>Edit user</h1>

<form method="post">
    <div asp-validation-summary="ModelOnly" class="text-danger"></div>

    <input asp-for="UserToEdit.ID" />

    <div class="form-group">
        <label asp-for="UserToEdit.Name" class="control-label"></label>
        <input asp-for="UserToEdit.Name" class="form-control" />
        <span asp-validation-for="UserToEdit.Name" class="text-danger"></span>
    </div>

    <div class="form-group">
        <label asp-for="UserToEdit.Department" class="control-label"></label>
        <select asp-for="UserToEdit.Department" class="form-control" asp-items="@Model.DepartmentsSL">
            <option value="">Choose department</option>
        </select>
        <span asp-validation-for="UserToEdit.Department" class="text-danger"></span>
    </div>

    <div class="form-group">
        <input type="submit" value="Save" class="btn btn-primary" />
        <a asp-page="./Index" class="btn btn-secondary">Cancel</a>
    </div>
</form>

您可能已经在PageModel中注意到了,我正在使用自定义modelbinder。我写这个是为了在视图发回时正确设置用户的部门。

public class UserModelBinder : IModelBinder
{
    private readonly IUserRepository _userRepository;

    public UserModelBinder(IUserRepository userRepository)
    {
        _userRepository = userRepository;
    }

    public Task BindModelAsync(ModelBindingContext bindingContext)
    {
        if (bindingContext == null)
        {
            throw new ArgumentNullException("bindingContext");
        }

        var modelName = bindingContext.ModelName;

        var user = new User();

        // ID.
        var idValue = bindingContext.ValueProvider.GetValue(modelName + ".ID");
        if (idValue != ValueProviderResult.None)
        {
            bindingContext.ModelState.SetModelValue(modelName + ".ID", idValue);
            var idString = idValue.FirstValue;
            if (!String.IsNullOrEmpty(idString))
            {
                var id = Convert.ToInt32(idString);
                user.ID = id;
            }
        }

        // Department.
        var departmentValue = bindingContext.ValueProvider.GetValue(modelName + ".Department");
        if (departmentValue != ValueProviderResult.None)
        {
            bindingContext.ModelState.SetModelValue(modelName + ".Department", departmentValue);
            var departmentIdString = departmentValue.FirstValue;
            if (!String.IsNullOrEmpty(departmentIdString))
            {
                var departmentId = Convert.ToInt32(departmentIdString);
                user.Department = _userRepository.GetDepartment(departmentId);
            }
        }

        bindingContext.Result = ModelBindingResult.Success(user);
        return Task.CompletedTask;
    }
}

现在,所有这些似乎都可以正常工作-我可以更改名称和部门,并且可以正确地发回。验证也可以。但是,当打开此编辑视图时,未在选择列表中选择用户的当前部门。

在EditModel.PopulateSelectList()中,我尝试了不同的方法,但是似乎都没有用。

DepartmentsSL = new SelectList(departments, nameof(Department.ID), nameof(Department.Name), UserToEdit.Department);
DepartmentsSL = new SelectList(departments, nameof(Department.ID), nameof(Department.Name), UserToEdit.Department.ID);
DepartmentsSL = new SelectList(departments, nameof(Department.ID), nameof(Department.Name), UserToEdit.Department.Name);

是否可以解决最后一部分?

到目前为止,我所看到的解决方案是在PageModel中具有一个单独的属性“ DepartmentID”并绑定到该属性,然后在回发后设置User的Department属性。我想那也意味着我可以摆脱自定义模型联编程序,但是我真的很想看看我当前的解决方案是否可以一路采用。 :)

asp.net-core model-binding razor-pages
1个回答
0
投票

打开此编辑视图时,未在选择列表中选择用户的当前部门

您可以尝试从SelectTagHelper中删除asp-for="UserToEdit.Department"并手动设置name="UserToEdit.Department",如下所示。

<select name="UserToEdit.Department" class="form-control" asp-items="@Model.DepartmentsSL">
    <option value="">Choose department</option>
</select>

并为SelectList设置selectedValue,如下所示。

DepartmentsSL = new SelectList(departments, nameof(Department.ID), nameof(Department.Name), UserToEdit.Department.ID);

测试结果

enter image description here

此外,另一种可能的方法是创建custom tag helper来渲染元素并根据您的实际模型数据和要求设置选定的值。

© www.soinside.com 2019 - 2024. All rights reserved.