diff --git a/FluentBootstrap.Mvc/Forms/MvcFormExtensions.cs b/FluentBootstrap.Mvc/Forms/MvcFormExtensions.cs index 9cfaf5e..24263a2 100644 --- a/FluentBootstrap.Mvc/Forms/MvcFormExtensions.cs +++ b/FluentBootstrap.Mvc/Forms/MvcFormExtensions.cs @@ -298,9 +298,15 @@ public static ValidationSummary IncludePropertyErrors(this Valid { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.GetConfig().HtmlHelper.ViewData); string expressionText = ExpressionHelper.GetExpressionText(expression); - string name = GetControlName(helper, expressionText); - string label = GetControlLabel(metadata, expressionText); - return helper.Select(name, label, options); + string name = GetControlName(helper, expressionText); + string label = GetControlLabel(metadata, expressionText); + ComponentBuilder, Select> builder = helper.Select(name, label); + if (metadata.Model != null && !string.IsNullOrEmpty(name)) + { + // Add the model value before adding options so they'll get selected on a match + builder.GetComponent().ModelValue = metadata.Model.ToString(); + } + return builder.AddOptions(options); } public static ComponentBuilder, Select> SelectFor( @@ -310,8 +316,14 @@ public static ValidationSummary IncludePropertyErrors(this Valid ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.GetConfig().HtmlHelper.ViewData); string expressionText = ExpressionHelper.GetExpressionText(expression); string name = GetControlName(helper, expressionText); - string label = GetControlLabel(metadata, expressionText); - return helper.Select(name, label, options); + string label = GetControlLabel(metadata, expressionText); + ComponentBuilder, Select> builder = helper.Select(name, label); + if (metadata.Model != null && !string.IsNullOrEmpty(name)) + { + // Add the model value before adding options so they'll get selected on a match + builder.GetComponent().ModelValue = metadata.Model.ToString(); + } + return builder.AddOptions(options); } public static ComponentBuilder, Select> SelectFor( @@ -320,9 +332,15 @@ public static ValidationSummary IncludePropertyErrors(this Valid { ModelMetadata metadata = ModelMetadata.FromLambdaExpression(expression, helper.GetConfig().HtmlHelper.ViewData); string expressionText = ExpressionHelper.GetExpressionText(expression); - string name = GetControlName(helper, expressionText); - string label = GetControlLabel(metadata, expressionText); - return helper.Select(name, label, selectList); + string name = GetControlName(helper, expressionText); + string label = GetControlLabel(metadata, expressionText); + ComponentBuilder, Select> builder = helper.Select(name, label); + if (metadata.Model != null && !string.IsNullOrEmpty(name)) + { + // Add the model value before adding options so they'll get selected on a match + builder.GetComponent().ModelValue = metadata.Model.ToString(); + } + return builder.AddOptions(selectList); } public static ComponentBuilder, Select> Select( @@ -336,9 +354,13 @@ public static ValidationSummary IncludePropertyErrors(this Valid public static ComponentBuilder, Select> AddOptions( this ComponentBuilder, Select> builder, IEnumerable selectList) { - foreach (SelectListItem item in selectList) + if (selectList != null) { - builder.AddChild(x => x.SelectOption(item.Text, item.Value, item.Selected)); + foreach (SelectListItem item in selectList) + { + var item1 = item; // Avoid foreach variable access in closure + builder.AddChild(x => x.SelectOption(item1.Text, item1.Value, item1.Selected)); + } } return builder; } diff --git a/FluentBootstrap.Tests.Web/Controllers/MvcTestsController.cs b/FluentBootstrap.Tests.Web/Controllers/MvcTestsController.cs index 89aa72c..769af47 100644 --- a/FluentBootstrap.Tests.Web/Controllers/MvcTestsController.cs +++ b/FluentBootstrap.Tests.Web/Controllers/MvcTestsController.cs @@ -15,7 +15,8 @@ public virtual ActionResult MvcTests(string view) { PropA = "A", PropB = 2, - PropC = new Dictionary() + PropC = "Two", + PropCOptions = new Dictionary() { { 1, "One"}, { 2, "Two"}, diff --git a/FluentBootstrap.Tests.Web/Models/MvcTests/ViewModel.cs b/FluentBootstrap.Tests.Web/Models/MvcTests/ViewModel.cs index b87c2f4..953b878 100644 --- a/FluentBootstrap.Tests.Web/Models/MvcTests/ViewModel.cs +++ b/FluentBootstrap.Tests.Web/Models/MvcTests/ViewModel.cs @@ -11,7 +11,8 @@ public class ViewModel [Display(Name = "Property A")] public string PropA { get; set; } public int PropB { get; set; } - public Dictionary PropC { get; set; } + public string PropC { get; set; } + public Dictionary PropCOptions { get; set; } public bool PropD { get; set; } } } \ No newline at end of file diff --git a/FluentBootstrap.Tests.Web/Views/MvcTests/MvcForms.cshtml b/FluentBootstrap.Tests.Web/Views/MvcTests/MvcForms.cshtml index 3bb293d..91c382d 100644 --- a/FluentBootstrap.Tests.Web/Views/MvcTests/MvcForms.cshtml +++ b/FluentBootstrap.Tests.Web/Views/MvcTests/MvcForms.cshtml @@ -57,7 +57,7 @@ { using (var form = Html.Bootstrap().Form().HideValidationSummary().Begin()) { - @form.DisplayListFor(x => x.PropC.Values) + @form.DisplayListFor(x => x.PropCOptions.Values) } } @@ -68,7 +68,7 @@ { using (var form = Html.Bootstrap().Form().HideValidationSummary().Begin()) { - @form.EditorListFor(x => x.PropC.Values) + @form.EditorListFor(x => x.PropCOptions.Values) } } @@ -125,7 +125,18 @@ { using (var form = Html.Bootstrap().Form().HideValidationSummary().Begin()) { - @form.SelectFor(x => x.PropC, Model.PropC.Select(y => new KeyValuePair(y.Key.ToString(), y.Value))) + @form.SelectFor(x => x.PropC, Model.PropCOptions.Select(y => new KeyValuePair(y.Key.ToString(), y.Value))) + } + } + +
+ + @Html.Bootstrap().Heading1("Select For With Add Options") + using (Html.Bootstrap().Div().SetId("test-select-for-with-add-options").Begin()) + { + using (var form = Html.Bootstrap().Form().HideValidationSummary().Begin()) + { + @form.SelectFor(x => x.PropC).AddOptions(Model.PropCOptions.Select(y => new KeyValuePair(y.Key.ToString(), y.Value))) } } } \ No newline at end of file diff --git a/FluentBootstrap.Tests/MvcFormsFixture.cs b/FluentBootstrap.Tests/MvcFormsFixture.cs index 58fb113..5d50da0 100644 --- a/FluentBootstrap.Tests/MvcFormsFixture.cs +++ b/FluentBootstrap.Tests/MvcFormsFixture.cs @@ -66,20 +66,20 @@ public void EditorOrDisplayForProducesCorrectHtml() TestHelper.AssertMvcHtml("test-display-list-for", @"
- -
+ +
"); } [Test] - public void DisplayListForForProducesCorrectHtml() + public void DisplayListForProducesCorrectHtml() { TestHelper.AssertMvcHtml("test-editor-list-for", @"
- +
@@ -90,7 +90,7 @@ public void DisplayListForForProducesCorrectHtml() // So that part of the HTML has been removed for now - these will have to be verified manually [Test] - public void InputForForProducesCorrectHtml() + public void InputForProducesCorrectHtml() { TestHelper.AssertMvcHtml("test-input-for", @" @@ -102,7 +102,7 @@ public void InputForForProducesCorrectHtml() } [Test] - public void PasswordForForProducesCorrectHtml() + public void PasswordForProducesCorrectHtml() { TestHelper.AssertMvcHtml("test-password-for", @" @@ -114,7 +114,7 @@ public void PasswordForForProducesCorrectHtml() } [Test] - public void CheckBoxForForProducesCorrectHtml() + public void CheckBoxForProducesCorrectHtml() { TestHelper.AssertMvcHtml("test-checkbox-for", @" @@ -130,7 +130,7 @@ public void CheckBoxForForProducesCorrectHtml() } [Test] - public void RadioForForProducesCorrectHtml() + public void RadioForProducesCorrectHtml() { TestHelper.AssertMvcHtml("test-radio-for", @" @@ -160,9 +160,26 @@ public void RadioForForProducesCorrectHtml() // HtmlAgilityPack strips the closing tag - very annoying!! [Test] - public void SelectForForProducesCorrectHtml() + public void SelectForProducesCorrectHtml() { TestHelper.AssertMvcHtml("test-select-for", +@" +
+ + +
+ "); + } + + // HtmlAgilityPack strips the closing tag - very annoying!! + [Test] + public void SelectForWithAddOptionsProducesCorrectHtml() + { + TestHelper.AssertMvcHtml("test-select-for-with-add-options", @"
diff --git a/FluentBootstrap.Tests/TestHelper.cs b/FluentBootstrap.Tests/TestHelper.cs index f729f4a..885bd8a 100644 --- a/FluentBootstrap.Tests/TestHelper.cs +++ b/FluentBootstrap.Tests/TestHelper.cs @@ -75,7 +75,8 @@ public static void AssertMvcHtml(string containerId, string expected, boo { PropA = "A", PropB = 2, - PropC = new Dictionary() + PropC = "Two", + PropCOptions = new Dictionary() { { 1, "One"}, { 2, "Two"}, diff --git a/FluentBootstrap/Forms/FormExtensions.cs b/FluentBootstrap/Forms/FormExtensions.cs index 18b6b3f..6ab4085 100644 --- a/FluentBootstrap/Forms/FormExtensions.cs +++ b/FluentBootstrap/Forms/FormExtensions.cs @@ -315,7 +315,8 @@ public static class FormExtensions { foreach (string option in options) { - builder.AddChild(x => x.SelectOption(option)); + var option1 = option; // Avoid foreach variable access in closure + builder.AddChild(x => x.SelectOption(option1)); } return builder; } @@ -325,7 +326,9 @@ public static class FormExtensions { foreach (KeyValuePair option in options) { - builder.AddChild(x => x.SelectOption(option.Key, option.Value)); + var option1 = option; // Avoid foreach variable access in closure + builder.AddChild(x => x.SelectOption(option1.Key, option1.Value, + builder.Component.ModelValue != null && string.Equals(builder.Component.ModelValue, option1.Value, StringComparison.OrdinalIgnoreCase))); } return builder; } diff --git a/FluentBootstrap/Forms/Select.cs b/FluentBootstrap/Forms/Select.cs index 7aab550..71b4eb3 100644 --- a/FluentBootstrap/Forms/Select.cs +++ b/FluentBootstrap/Forms/Select.cs @@ -13,6 +13,9 @@ public class Select : FormControl, IHasNameAttribute, { public bool Multiple { get; set; } + // This is needed so that when adding options, we can check if they match the model value and select if so + public string ModelValue { get; set; } + internal Select(BootstrapHelper helper) : base(helper, "select", Css.FormControl) {