【问题标题】:Passing radio button values to the controller将单选按钮值传递给控制器
【发布时间】:2020-07-06 18:24:04
【问题描述】:

我有类似这样的单选按钮

@foreach (ItemHasParts ihp in item.IHP)
{
    Part part = db.Parts.Find(ihp.PartID);
    string partID = part.ID.ToString();
    <tr>
        <td>
            @part.PartIDLink
        </td>
        <td>
            @Html.DisplayFor(modelPart => part.MFG_number)
        </td>
        <td>
            @Html.DisplayFor(modelPart => part.Name)
        </td>
        <td>
            @Html.RadioButton(partID, "Harvest")
        </td>
        <td>
            @Html.RadioButton(partID, "Transfer")
        </td>
        <td>
            @Html.RadioButton(partID, "Dispose")
        </td>
    </tr>
}

这是我的控制器方法

public ActionResult SpecialOrderSummary(JobOrder job, FormCollection form)
{
    //list of parts that were chosen to be transferred
    //list of parts that were chosen to be harvested
    //list of parts that were chosen to be disposed        
    return RedirectToAction("Details", "JobOrders", new { id = job.ID });
}

它循环并为每个部分生成单选按钮。如何让我的控制器检索用户选择的每个单选按钮的值?

【问题讨论】:

  • 返回视图是什么?你的视图模型是什么(或者你有)?
  • @DavidLiang 我真的没有这个场景的视图模型,因为我不知道它会如何处理它。有什么建议吗?我有一个工作订单模型和一个分解“工作边界”的 jodetails 模型,但这是为常规工作订单设置的。我正在做的是一个不同版本的工作订单,所以这个转移/处置/收获是一个全新的过程
  • @DavidLiang 实际上我想我可以使用现有的 JODetails 视图模型并进行相应调整。我已经在我的模型中添加了我的问题
  • 您应该在视图和控制器之间传递视图模型的原因是,除了在视图上显示所需的所有数据之外,您还可以声明属性以包含用户选择。因此,在您的情况下,您可以声明选定类型的选定部件列表。无论如何,将数据库模型直接传递给视图并不是一个好习惯。
  • 而且你刚刚发布的JODetails不叫视图模型。那是您的数据库模型,或您的业务模型。视图不需要渲染所有信息。您只是向视图透露了太多信息。最好只新建一个视图模型并声明您需要传递给视图的唯一必要属性。

标签: javascript c# html asp.net-mvc razor


【解决方案1】:

最好不要将后端模型暴露给视图/前端。相反,您是否应该构建一个仅包含视图需要呈现的数据并在视图和控制器之间传递数据的视图模型。

您还可以在视图模型中声明其他属性以适应您的需要。在您的情况下,我可以声明一个属性来包含每个部分的用户选择。

让我告诉你我的意思。

首先,我会将魔术字符串“Harvest”、“Transfer”和“Dispose”转换为枚举,因为我不喜欢处理魔术字符串:

namespace DL.SO.RadiosToController.WebUI.Models.Item
{
    public enum PartActionType
    {
        Harvest,
        Transfer,
        Dispose
    }
}

然后你可以制作出你需要的视图模型:

namespace DL.SO.RadiosToController.WebUI.Models.Item
{
    public class ItemViewModel
    {
        [Required]
        public int ItemId { get; set; }
        public string ItemName { get; set; }
        public string MFGNumber { get; set; }
        public IList<ItemPartViewModel> Parts { get; set; }
    }
    
    public class ItemPartViewModel
    {
        [Required]
        public int PartId { get; set; }
        public string MFGNumber { get; set; }
        public string PartName { get; set; }

        // This is the additional property to contain what user picks
        public PartActionType SelectedActionType { get; set; }
    }
}

按照惯例,这些视图模型以及枚举应该在您的控制器模型视图中。假设您有一个 ItemController 来显示项目,并且有方法可以在表单发布上保存用户选择,这些视图模型可以在文件夹 /Models/Item/ 中。

我只是根据需要标记了他们的 ID,因为我猜您需要在表单发布时使用 ID,以便从持久性存储中检索数据。

然后您从持久存储中检索数据,构建视图模型并将其从控制器返回到视图:

namespace DL.SO.RadiosToController.WebUI.Controllers
{
    public class ItemController : Controller
    {
        public ActionResult Index()
        {
            // Retrieve data from persistence storage and save it to the view model.
            // But here I am just faking it.
            var vm = new ItemViewModel
            {
                ItemId = 123,
                ItemName = "Fake Item",
                Parts = new List<ItemPartViewModel>
                {
                    new ItemPartViewModel
                    {
                        PartId = 1,
                        PartName = "Part 1"
                    },
                    new ItemPartViewModel
                    {
                        PartId = 2,
                        PartName = "Part 2"
                    },
                    new ItemPartViewModel
                    {
                        PartId = 3,
                        PartName = "Part 3"
                    },
                    new ItemPartViewModel
                    {
                        PartId = 4,
                        PartName = "Part 4"
                    }
                }
            };

            return View(vm);
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Save(ItemViewModel model)
        {
            // Do what you need to do
            return View();
        }
    }
}

最后你可以构造视图:

<!-- 
    Here I create an alias for the namespace so that I don't have to type
    the long namespace every time.
-->
@using PartActionType = DL.SO.RadiosToController.WebUI.Models.Item.PartActionType

<!-- Here I declare the model for this view. -->
@model DL.SO.RadiosToController.WebUI.Models.Item.ItemViewModel
@{
    ViewBag.Title = "Items";

    <!-- I am getting the enum values and assign them to this local variable -->
    var partActionTypes = Enum.GetValues(typeof(PartActionType));
}

@using (Html.BeginForm("save", "item", FormMethod.Post))
{
    @Html.AntiForgeryToken()

    @Html.HiddenFor(x => x.ItemId)

    <table class="table table-sm text-center">
        <thead>
            <tr>
                <th>Item ID</th>
                <th>MFG ID</th>
                <th>Item Name</th>
            </tr>
        </thead>
        <tbody>
            <tr>
                <td>@Model.ItemId</td>
                <td>@Model.MFGNumber</td>
                <td>@Model.ItemName</td>
            </tr>
        </tbody>
    </table>

    <h5>Parts In Items (@Model.Parts.Count)</h5>

    <table class="table table-sm text-center">
        <thead>
            <tr>
                <th>Part ID</th>
                <th>MFG ID</th>
                <th>Part Name</th>

                <!-- I'm displaying the names for the num dynamically. -->
                @foreach (var actionType in partActionTypes)
                {
                    <th>@Enum.GetName(typeof(PartActionType), actionType)</th>
                }
            </tr>
        </thead>
        <tbody>
            @for (int i = 0; i < Model.Parts.Count; i++)
            {
                <tr>
                    <td>
                        @Html.DisplayFor(x => x.Parts[i].PartId)
                        @Html.HiddenFor(x => x.Parts[i].PartId)
                    </td>
                    <td>
                        @Html.DisplayFor(x => x.Parts[i].MFGNumber)
                        @Html.HiddenFor(x => x.Parts[i].MFGNumber)
                    </td>
                    <td>
                        @Html.DisplayFor(x => x.Parts[i].PartName)
                        @Html.HiddenFor(x => x.Parts[i].PartName)
                    </td>
                    @foreach (var actionType in partActionTypes)
                    {
                        <td>
                            @Html.RadioButtonFor(x => x.Parts[i].SelectedActionType, actionType)
                        </td>
                    }
                </tr>
            }
        </tbody>
    </table>
    <button class="btn btn-success" type="submit">Save</button>
}

这里要注意的重要一点是,要构造部分行,我必须使用带索引的常规 for 循环,而不是使用常规的 @foreach()。原因是当您使用@foreach() 时,这些输入的名称将相同,并且当您将输入发送回服务器时,服务器不会将它们视为列表。您必须使用带有索引的常规 for 循环,以便输入的名称不同,因此它们被服务器捕获为列表中的不同项目:

<input id="Parts_2__SelectedActionType" name="Parts[2].SelectedActionType" 
  type="radio" value="Transfer">

您可能已经注意到那些@Html.HiddenFor()。默认情况下,表单会将任何输入发送回服务器。如果你想发布额外的数据,你可以使用@Html.HiddenFor(),它将呈现为一个隐藏的输入。

在表单发布中,您可以看到正确的单选按钮值已发布到服务器。


dotnet fiddle 演示: https://dotnetfiddle.net/wbGsbd

Github 中的示例代码: https://github.com/davidliang2008/DL.SO.RadiosToController

【讨论】:

    猜你喜欢
    • 2019-07-06
    • 2017-11-30
    • 2019-03-24
    • 2014-03-26
    • 1970-01-01
    • 2021-08-16
    • 1970-01-01
    • 2014-04-08
    • 1970-01-01
    相关资源
    最近更新 更多