如何使用单选按钮在MVC3中选择列表项

时间:2011-07-13 09:53:03

标签: asp.net asp.net-mvc asp.net-mvc-3 razor

我需要向用户显示一个包选项列表,然后从中选择一个。我不想使用单选按钮列表,因为我需要为每个列表项创建相当复杂的模板。原谅我,但我似乎无法弄清楚如何将一列单选按钮链接到我的视图模型中的选择属性。

我的视图模型有SelectedPackageId(int),以及代表各个包的MemberPackageListItem视图模型列表。 MemberPackageListItem有一个PackageId(int),因此我需要将所选项目的PackageId与根视图模型的SelectedPackageId结合起来。

我很难发布代码,因为继承等会掩盖你想要看到的大部分内容,所以我希望我勾勒出一个相当常见的场景,以及在这个场景中使用单选按钮的一些一般指导足以帮助我继续。

2 个答案:

答案 0 :(得分:2)

我建议使用HtmlHelper渲染单选按钮列表,如下所示:

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString RadioButtonListFor<TModel, TList, TSelectedItem>(
        this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TList>> expression,
        Expression<Func<TModel, TSelectedItem>> selectedItem)
    {
        return RadioButtonListFor(htmlHelper, expression, selectedItem, null /* htmlAttributes */);
    }

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString RadioButtonListFor<TModel, TList, TSelectedItem>(
        this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TList>> expression, 
        Expression<Func<TModel, TSelectedItem>> selectedItem, object htmlAttributes)
    {
        return RadioButtonListFor(htmlHelper, expression, selectedItem, HtmlHelper.AnonymousObjectToHtmlAttributes(htmlAttributes));
    }

    [SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Justification = "This is an appropriate nesting of generic types")]
    public static MvcHtmlString RadioButtonListFor<TModel, TList, TSelectedItem>(
        this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TList>> expression, 
        Expression<Func<TModel, TSelectedItem>> selectedItem, IDictionary<string, object> htmlAttributes)
    {
        if (expression == null)
        {
            throw new ArgumentNullException("expression");
        }

        ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData);
        IEnumerable<SelectListItem> items = null;
        if (metadata.Model != null)
        {
            IEnumerable<SelectListItem> modelItems = (IEnumerable<SelectListItem>)metadata.Model;
            if (modelItems != null)
            {
                items = modelItems;
            }
        }

        ModelMetadata selectedItemMetadata = ModelMetadata.FromLambdaExpression(selectedItem, htmlHelper.ViewData);

        return RadioButtonListHelper(htmlHelper, metadata, selectedItemMetadata, ExpressionHelper.GetExpressionText(selectedItem), items, htmlAttributes);
    }

    private static MvcHtmlString RadioButtonListHelper(HtmlHelper htmlHelper, ModelMetadata metadata, 
        ModelMetadata selectedItemMetadata, string name, IEnumerable<SelectListItem> selectList, IDictionary<string, object> htmlAttributes)
    {
        // Verify arguments
        if (String.IsNullOrEmpty(name)) throw new ArgumentNullException("name", "Name cannot be null");
        if (selectList == null) throw new ArgumentNullException("selectList", "Select list cannot be null");
        if (selectList.Count() < 1) throw new ArgumentException("Select list must contain at least one value", "selectList");

        string fullName = htmlHelper.ViewContext.ViewData.TemplateInfo.GetFullHtmlFieldName(name);
        string fullId = htmlHelper.ViewData.TemplateInfo.HtmlFieldPrefix + "_" + name;

        IDictionary<string, object> validationAttributes = htmlHelper
            .GetUnobtrusiveValidationAttributes(ExpressionHelper.GetExpressionText(name), selectedItemMetadata);

        // Define items
        StringBuilder items = new StringBuilder();

        // Loop through items
        Int32 index = 0;
        foreach (SelectListItem i in selectList)
        {

            // Define check box input
            TagBuilder input = new TagBuilder("input");
            input.MergeAttribute("type", "radio");
            input.MergeAttribute("name", fullName, true);
            if (i.Selected)
                input.MergeAttribute("checked", "checked");
            input.MergeAttribute("value", i.Value);
            if (index == 0)
                input.MergeAttributes(validationAttributes);
            input.MergeAttributes(htmlAttributes);

            // Define label
            TagBuilder label = new TagBuilder("label");
            label.MergeAttribute("for", fullId + "[" + index.ToString() + "].Selected");
            label.InnerHtml = i.Text;

            // Add item
            items.AppendFormat("\r\t<div>\r\t\t{0}\r\t\t{1}\r\t</div>",
                input.ToString(TagRenderMode.Normal),
                label.ToString(TagRenderMode.Normal));

            index++;
        }
        // Return list
        return new MvcHtmlString(items.ToString() + "\r");
    }

请注意,MemberPackageListItem必须是IEnumerable<SelectListItem>类型。用法如下(Razor语法):

    @Html.RadioButtonListFor(m => m.MemberPackageListItem, m => m.SelectedPackageId)

counsellorben

答案 1 :(得分:1)

虽然我很欣赏@consllorben的答案的技术全面性,并将保留它以备将来使用,但今天我得到了一个更直接而不是完全笨拙的jQuery解决方案。选择器可能更具体,但我现在不需要。我的解决方案如下。无线电类型输入按其名称属性分组,每个行都有不同的索引。因此:

$(function () {
    // Back up current names of package radio buttons, then make all their names the same for grouping.
    $("#packageForm :radio[name$='.IsSelected']").each(function () {
        $(this).attr("oldname", $(this).attr("name"));
    });
    $(":radio[name$='.IsSelected']").attr("name", "Package.IsSelected");

    // Hook the 'submit' click to restore original radio button names.
    $("#packageForm :submit").click(function () {
        $(":radio[name='Package.IsSelected']").each(function () {
            $(this).attr("oldname", $(this).attr("name"));
        });
    });
});  

IsSelected是我的每行属性,告诉我是否选择了该行,它不是jQuery或DOM属性。