Skip to content

Update SelectExtensions to handle nested expressions where no name is parsed #166

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 23, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/System.Web.Mvc/Html/SelectExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ private static MvcHtmlString ListBoxHelper(HtmlHelper htmlHelper, ModelMetadata
private static IEnumerable<SelectListItem> GetSelectData(this HtmlHelper htmlHelper, string name)
{
object o = null;
if (htmlHelper.ViewData != null)
if (htmlHelper.ViewData != null && !String.IsNullOrEmpty(name))
{
o = htmlHelper.ViewData.Eval(name);
}
Expand Down Expand Up @@ -396,9 +396,9 @@ private static MvcHtmlString SelectInternal(this HtmlHelper htmlHelper, ModelMet

// If we haven't already used ViewData to get the entire list of items then we need to
// use the ViewData-supplied value before using the parameter-supplied value.
if (defaultValue == null && !String.IsNullOrEmpty(name))
if (defaultValue == null)
{
if (!usedViewData)
if (!usedViewData && !String.IsNullOrEmpty(name))
{
defaultValue = htmlHelper.ViewData.Eval(name);
}
Expand Down
74 changes: 74 additions & 0 deletions test/System.Web.Mvc.Test/Html/Test/SelectExtensionsTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ public class SelectExtensionsTest : IDisposable
{
private static readonly ViewDataDictionary<FooModel> _listBoxViewData = new ViewDataDictionary<FooModel> { { "foo", new[] { "Bravo" } } };
private static readonly ViewDataDictionary<FooModel> _dropDownListViewData = new ViewDataDictionary<FooModel> { { "foo", "Bravo" } };
private static readonly ViewDataDictionary<FooContainerModel> _nestedDropDownListViewData = new ViewDataDictionary<FooContainerModel> { { "foo", "Bravo" } };
private static readonly ViewDataDictionary<NonIEnumerableModel> _nonIEnumerableViewData = new ViewDataDictionary<NonIEnumerableModel> { { "foo", 1 } };
private static readonly ViewDataDictionary<EnumModel> _enumDropDownListViewData = new ViewDataDictionary<EnumModel>
{
Expand Down Expand Up @@ -1191,6 +1192,48 @@ public void DropDownListForUsesLambdaDefaultValue()
+ "</select>",
html.ToHtmlString());
}

[Fact]
public void DropDownListForUsesLambdaDefaultValueWhenNested()
{
// Arrange
FooModel model = new FooModel { foo = "Bravo" };
ViewDataDictionary<FooModel> viewData = new ViewDataDictionary<FooModel>(model);
ViewDataDictionary<string> nestedViewData = MvcHelper.GetNestedViewData(viewData, m => m.foo);
HtmlHelper<string> helper = MvcHelper.GetHtmlHelper(nestedViewData);
SelectList selectList = new SelectList(MultiSelectListTest.GetSampleStrings());

// Act
MvcHtmlString html = helper.DropDownListFor(m => m, selectList);

// Assert
Assert.Equal(
"<select id=\"foo\" name=\"foo\"><option>Alpha</option>" + Environment.NewLine
+ "<option selected=\"selected\">Bravo</option>" + Environment.NewLine
+ "<option>Charlie</option>" + Environment.NewLine
+ "</select>",
html.ToHtmlString());
}

[Fact]
public void DropDownListForUsesLambdaDefaultValueFromViewDataWhenNested()
{
// Arrange
ViewDataDictionary<FooModel> nestedViewData = MvcHelper.GetNestedViewData(_nestedDropDownListViewData, m => m.inner);
HtmlHelper<FooModel> helper = MvcHelper.GetHtmlHelper(nestedViewData);
SelectList selectList = new SelectList(MultiSelectListTest.GetSampleStrings());

// Act
MvcHtmlString html = helper.DropDownListFor(m => m.foo, selectList);

// Assert
Assert.Equal(
"<select id=\"inner_foo\" name=\"inner.foo\"><option>Alpha</option>" + Environment.NewLine
+ "<option selected=\"selected\">Bravo</option>" + Environment.NewLine
+ "<option>Charlie</option>" + Environment.NewLine
+ "</select>",
html.ToHtmlString());
}

[Fact]
public void DropDownListForUsesLambdaDefaultValueWithNullSelectListUsesViewData()
Expand All @@ -1215,6 +1258,33 @@ public void DropDownListForUsesLambdaDefaultValueWithNullSelectListUsesViewData(
html.ToHtmlString());
}

[Fact]
public void DropDownListForUsesLambdaDefaultValueWithNullSelectListUsesViewDataWhenNested()
{
// Arrange
FooContainerModel model = new FooContainerModel { inner = new FooModel { foo = "Bravo" } };
ViewDataDictionary<FooContainerModel> vdd = new ViewDataDictionary<FooContainerModel>(model)
{
{ "foo", new SelectList(MultiSelectListTest.GetSampleStrings()) }
};

ViewDataDictionary<FooModel> nestedViewData = MvcHelper.GetNestedViewData(vdd, m => m.inner);


HtmlHelper<FooModel> helper = MvcHelper.GetHtmlHelper(nestedViewData);

// Act
MvcHtmlString html = helper.DropDownListFor(m => m.foo, selectList: null);

// Assert
Assert.Equal(
"<select id=\"inner_foo\" name=\"inner.foo\"><option>Alpha</option>" + Environment.NewLine
+ "<option selected=\"selected\">Bravo</option>" + Environment.NewLine
+ "<option>Charlie</option>" + Environment.NewLine
+ "</select>",
html.ToHtmlString());
}

[Fact]
public void DropDownListForWithAttributesDictionary()
{
Expand Down Expand Up @@ -3330,6 +3400,10 @@ private static IEnumerable<SelectListItem> GetSelectListWithNumericValuesForEnum
return selectList;
}

private class FooContainerModel
{
public FooModel inner { get; set; }
}
private class FooModel
{
public string foo { get; set; }
Expand Down
20 changes: 20 additions & 0 deletions test/System.Web.Mvc.Test/Util/MvcHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using System.Collections;
using System.IO;
using System.Linq.Expressions;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
Expand Down Expand Up @@ -152,6 +153,25 @@ public static HttpContextBase GetHttpContext(string appPath, string requestPath,
return GetHttpContext(appPath, requestPath, httpMethod, Uri.UriSchemeHttp.ToString(), -1);
}

public static ViewDataDictionary<TValue> GetNestedViewData<TModel, TValue>(ViewDataDictionary<TModel> viewData, Expression<Func<TModel, TValue>> expression)
{
var metadata = ModelMetadata.FromLambdaExpression(expression, viewData);
var htmlFieldName = ExpressionHelper.GetExpressionText(expression);


ViewDataDictionary nestedViewData = new ViewDataDictionary(viewData)
{
Model = metadata.Model,
ModelMetadata = metadata,
TemplateInfo = new TemplateInfo
{
HtmlFieldPrefix = viewData.TemplateInfo.GetFullHtmlFieldName(htmlFieldName),
}
};

return new ViewDataDictionary<TValue>(nestedViewData);
}

public static ViewContext GetViewContextWithPath(string appPath, ViewDataDictionary viewData)
{
HttpContextBase httpContext = GetHttpContext(appPath, "/request", "GET");
Expand Down