ASP.NET Razor @for 循环中对象列表的 MVC 绑定唯一单选按钮,只允许选择一个

ASP.NET MVC Binding unique radio button for list of objects in Razor @for loop, allowing only one to be selected

我有这样的模型:

public class ImageFileDetail {
    public HttpPostedFileBase File { get; set; }
    public bool IsMainImage { get; set; }
    public string SourceURL { get; set; }
}

我用计数为 10 的 List<ImageFileDetail> 填充剃刀视图,允许用户上传 10 张图像。 IsMainImage.

只能一张图片

我必须在 @for 循环而不是 @foreach 中执行此操作,否则模型绑定器不会绑定到列表中的每个单独对象。执行 @foreach 会在发布表单时为它们提供相同的 name=,因此我无法将其作为单独的对象进行迭代。

查看:

@for (var i = 0; i < Model.ImageFileDetails.Count(); i++)
{
    <tr>
        <td>
            @Html.TextBoxFor(x => x.ImageFileDetails[i].File, new { type = "file" })
        </td>
        <td>
            @Html.EditorFor(x => x.ImageFileDetails[i].SourceURL, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(x => x.ImageFileDetails[i].SourceURL, "", new { @class = "text-danger" })
        </td>
        <td>
            @*Html.EditorFor(x => x.ImageFileDetails[i].IsMainImage)*@
            // this is the radio button
            @Html.RadioButton("UniqueIsMainImage", Model.ImageFileDetails[i].IsMainImage)
            @Html.ValidationMessageFor(x => x.ImageFileDetails[i].IsMainImage, "", new { @class = "text-danger" })
        </td>
    </tr>
}

通过在 @RadioButton 中为单选按钮指定相同的名称,这样我就可以只选择一个,这样很好。当一个被选中时,前一个被取消选择,这是想要的结果。

问题是在 POST 上,我没有得到列表中所选单选按钮的值 true,我不确定为什么。

我做错了什么?

我必须将单选按钮更改为 @Html.RadioButtonFor(x => Model.SelectedImageId, Model.ImageFileDetails[i].Id) 并为列表中的每个对象传递一个 Id

视图模型:

public class ViewModel
{
    public List<ImageFileDetail> ImageFileDetails { get; set; } = new List<ImageFileDetail>();
    public int? SelectedImageId { get; set; }
}

public class ImageFileDetail {
    public int Id { get; set; } // for main image radio button
    public HttpPostedFileBase File { get; set; }
    public bool IsMainImage { get; set; }
    public string SourceURL { get; set; }
}

查看:

@for (var i = 0; i < Model.ImageFileDetails.Count(); i++)
{
    <tr>
        <td>
            @Html.TextBoxFor(x => x.ImageFileDetails[i].File, new { type = "file" })
        </td>
        <td>
            @Html.EditorFor(x => x.ImageFileDetails[i].SourceURL, new { htmlAttributes = new { @class = "form-control" } })
            @Html.ValidationMessageFor(x => x.ImageFileDetails[i].SourceURL, "", new { @class = "text-danger" })
        </td>
        <td>
            @*Html.EditorFor(x => x.ImageFileDetails[i].IsMainImage)*@
            @Html.RadioButtonFor(x => Model.SelectedImageId, Model.ImageFileDetails[i].Id)
            @Html.ValidationMessageFor(x => x.ImageFileDetails[i].IsMainImage, "", new { @class = "text-danger" })
        </td>
    </tr>
}

然后在我的控制器中,当遍历发布的列表时,我将 SelectedImageIdIsMainImage 设置为 true。

Post 控制器:

foreach (var fileDetail in viewModel.ImageFileDetails)
{       
        if (fileDetail.Id == viewModel.SelectedImageId)
            fileDetail.IsMainImage = true;      
}

感谢大家对我的信任

这可能不完全是您要查找的内容,但与您的 for/foreach 声明有关 - 如果您愿意,razor 实际上会为您执行此操作:

另外我认为你应该使用 RadioButtonFor。

您可以利用 razor 视图中的模板,让它为您处理 list/array。所以而不是:

@Html.EditorFor(x => x.ImageFileDetails[i].SourceURL,...

您应该这样做(注意模板文件的名称应与 TYPE 名称匹配):

@Html.EditorFor(x => x.ImageFileDetails)

然后创建一个 EditorTemplates/ImageFileDetail.cshtml 模板:

@model <namespace>.ImageFileDetail

 <tr>
    <td>
        @Html.TextBoxFor(x => x.File, new { type = "file" })
    </td>
    <td>
        @Html.EditorFor(x => x.SourceURL, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(x => x.SourceURL, "", new { @class = "text-danger" })
    </td>
    <td>
        @*Html.EditorFor(x => x.IsMainImage)*@
        // this is the radio button
        @Html.RadioButtonFor(x => x.IsMainImage, x.IsMainImage, new { name = "UniqueIsMainImage" })

        @Html.ValidationMessageFor(x => x.IsMainImage, "", new { @class = "text-danger" })
    </td>
</tr>