Skip to content

Commit 8b1a545

Browse files
committed
Update SelectExtensions to handle nested expressions where no name is parsed.
1 parent 8eb4d1c commit 8b1a545

File tree

3 files changed

+97
-3
lines changed

3 files changed

+97
-3
lines changed

src/System.Web.Mvc/Html/SelectExtensions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,7 @@ private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, ModelMetadata
285285
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
286286
{
287287
object o = null;
288-
if (htmlHelper.ViewData != null)
288+
if (htmlHelper.ViewData != null && !String.IsNullOrEmpty(name))
289289
{
290290
o = htmlHelper.ViewData.Eval(name);
291291
}
@@ -396,9 +396,9 @@ private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMet
396396

397397
// If we haven't already used ViewData to get the entire list of items then we need to
398398
// use the ViewData-supplied value before using the parameter-supplied value.
399-
if (defaultValue == null && !String.IsNullOrEmpty(name))
399+
if (defaultValue == null)
400400
{
401-
if (!usedViewData)
401+
if (!usedViewData && !String.IsNullOrEmpty(name))
402402
{
403403
defaultValue = htmlHelper.ViewData.Eval(name);
404404
}

test/System.Web.Mvc.Test/Html/Test/SelectExtensionsTest.cs

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ public class SelectExtensionsTest : IDisposable
1919
{
2020
private static readonly ViewDataDictionary<FooModel> _listBoxViewData = new ViewDataDictionary<FooModel> { { "foo", new[] { "Bravo" } } };
2121
private static readonly ViewDataDictionary<FooModel> _dropDownListViewData = new ViewDataDictionary<FooModel> { { "foo", "Bravo" } };
22+
private static readonly ViewDataDictionary<FooContainerModel> _nestedDropDownListViewData = new ViewDataDictionary<FooContainerModel> { { "foo", "Bravo" } };
2223
private static readonly ViewDataDictionary<NonIEnumerableModel> _nonIEnumerableViewData = new ViewDataDictionary<NonIEnumerableModel> { { "foo", 1 } };
2324
private static readonly ViewDataDictionary<EnumModel> _enumDropDownListViewData = new ViewDataDictionary<EnumModel>
2425
{
@@ -1191,6 +1192,48 @@ public void DropDownListForUsesLambdaDefaultValue()
11911192
+ "</select>",
11921193
html.ToHtmlString());
11931194
}
1195+
1196+
[Fact]
1197+
public void DropDownListForUsesLambdaDefaultValueWhenNested()
1198+
{
1199+
// Arrange
1200+
FooModel model = new FooModel { foo = "Bravo" };
1201+
ViewDataDictionary<FooModel> viewData = new ViewDataDictionary<FooModel>(model);
1202+
ViewDataDictionary<string> nestedViewData = MvcHelper.GetNestedViewData(viewData, m => m.foo);
1203+
HtmlHelper<string> helper = MvcHelper.GetHtmlHelper(nestedViewData);
1204+
SelectList selectList = new SelectList(MultiSelectListTest.GetSampleStrings());
1205+
1206+
// Act
1207+
MvcHtmlString html = helper.DropDownListFor(m => m, selectList);
1208+
1209+
// Assert
1210+
Assert.Equal(
1211+
"<select id=\"foo\" name=\"foo\"><option>Alpha</option>" + Environment.NewLine
1212+
+ "<option selected=\"selected\">Bravo</option>" + Environment.NewLine
1213+
+ "<option>Charlie</option>" + Environment.NewLine
1214+
+ "</select>",
1215+
html.ToHtmlString());
1216+
}
1217+
1218+
[Fact]
1219+
public void DropDownListForUsesLambdaDefaultValueFromViewDataWhenNested()
1220+
{
1221+
// Arrange
1222+
ViewDataDictionary<FooModel> nestedViewData = MvcHelper.GetNestedViewData(_nestedDropDownListViewData, m => m.inner);
1223+
HtmlHelper<FooModel> helper = MvcHelper.GetHtmlHelper(nestedViewData);
1224+
SelectList selectList = new SelectList(MultiSelectListTest.GetSampleStrings());
1225+
1226+
// Act
1227+
MvcHtmlString html = helper.DropDownListFor(m => m.foo, selectList);
1228+
1229+
// Assert
1230+
Assert.Equal(
1231+
"<select id=\"inner_foo\" name=\"inner.foo\"><option>Alpha</option>" + Environment.NewLine
1232+
+ "<option selected=\"selected\">Bravo</option>" + Environment.NewLine
1233+
+ "<option>Charlie</option>" + Environment.NewLine
1234+
+ "</select>",
1235+
html.ToHtmlString());
1236+
}
11941237

11951238
[Fact]
11961239
public void DropDownListForUsesLambdaDefaultValueWithNullSelectListUsesViewData()
@@ -1215,6 +1258,33 @@ public void DropDownListForUsesLambdaDefaultValueWithNullSelectListUsesViewData(
12151258
html.ToHtmlString());
12161259
}
12171260

1261+
[Fact]
1262+
public void DropDownListForUsesLambdaDefaultValueWithNullSelectListUsesViewDataWhenNested()
1263+
{
1264+
// Arrange
1265+
FooContainerModel model = new FooContainerModel { inner = new FooModel { foo = "Bravo" } };
1266+
ViewDataDictionary<FooContainerModel> vdd = new ViewDataDictionary<FooContainerModel>(model)
1267+
{
1268+
{ "foo", new SelectList(MultiSelectListTest.GetSampleStrings()) }
1269+
};
1270+
1271+
ViewDataDictionary<FooModel> nestedViewData = MvcHelper.GetNestedViewData(vdd, m => m.inner);
1272+
1273+
1274+
HtmlHelper<FooModel> helper = MvcHelper.GetHtmlHelper(nestedViewData);
1275+
1276+
// Act
1277+
MvcHtmlString html = helper.DropDownListFor(m => m.foo, selectList: null);
1278+
1279+
// Assert
1280+
Assert.Equal(
1281+
"<select id=\"inner_foo\" name=\"inner.foo\"><option>Alpha</option>" + Environment.NewLine
1282+
+ "<option selected=\"selected\">Bravo</option>" + Environment.NewLine
1283+
+ "<option>Charlie</option>" + Environment.NewLine
1284+
+ "</select>",
1285+
html.ToHtmlString());
1286+
}
1287+
12181288
[Fact]
12191289
public void DropDownListForWithAttributesDictionary()
12201290
{
@@ -3330,6 +3400,10 @@ private static IEnumerable<SelectListItem> GetSelectListWithNumericValuesForEnum
33303400
return selectList;
33313401
}
33323402

3403+
private class FooContainerModel
3404+
{
3405+
public FooModel inner { get; set; }
3406+
}
33333407
private class FooModel
33343408
{
33353409
public string foo { get; set; }

test/System.Web.Mvc.Test/Util/MvcHelper.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System;
55
using System.Collections;
66
using System.IO;
7+
using System.Linq.Expressions;
78
using System.Web;
89
using System.Web.Mvc;
910
using System.Web.Routing;
@@ -152,6 +153,25 @@ public static HttpContextBase GetHttpContext(string appPath, string requestPath,
152153
return GetHttpContext(appPath, requestPath, httpMethod, Uri.UriSchemeHttp.ToString(), -1);
153154
}
154155

156+
public static ViewDataDictionary<TValue> GetNestedViewData<TModel, TValue>(ViewDataDictionary<TModel> viewData, Expression<Func<TModel, TValue>> expression)
157+
{
158+
var metadata = ModelMetadata.FromLambdaExpression(expression, viewData);
159+
var htmlFieldName = ExpressionHelper.GetExpressionText(expression);
160+
161+
162+
ViewDataDictionary nestedViewData = new ViewDataDictionary(viewData)
163+
{
164+
Model = metadata.Model,
165+
ModelMetadata = metadata,
166+
TemplateInfo = new TemplateInfo
167+
{
168+
HtmlFieldPrefix = viewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName),
169+
}
170+
};
171+
172+
return new ViewDataDictionary<TValue>(nestedViewData);
173+
}
174+
155175
public static ViewContext GetViewContextWithPath(string appPath, ViewDataDictionary viewData)
156176
{
157177
HttpContextBase httpContext = GetHttpContext(appPath, "/request", "GET");

0 commit comments

Comments
 (0)