Skip to content

Commit 0184724

Browse files
committed
Merge pull request #132 from MRCollective/partialfor
@f.PartialFor, @f.Partial, @s.PartialFor, @s.Partial
2 parents f0cbc32 + f1d8abe commit 0184724

23 files changed

+662
-14
lines changed

BREAKING_CHANGES.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,19 @@ ChameleonForms Breaking Changes
44
Version 2.0.0
55
=============
66

7+
Deprecated `WithoutLabel` method on `IFieldConfiguration`. It still works (for now), but the method has been marked with the `[Obsolete]` attribute.
8+
9+
### Reason
10+
11+
The method has been renamed to `WithoutLabelElement` since it more closely reflects what the method does.
12+
13+
### Workaround
14+
15+
Change all instances of `WithoutLabel` to `WithoutLabelElement`.
16+
17+
Version 2.0.0
18+
=============
19+
720
Remove the `ReadOnlyConfiguration` public class.
821

922
### Reason

ChameleonForms.AcceptanceTests/ChameleonForms.AcceptanceTests.csproj

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@
3232
<WarningLevel>4</WarningLevel>
3333
</PropertyGroup>
3434
<ItemGroup>
35+
<Reference Include="ApprovalTests, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
36+
<HintPath>..\packages\ApprovalTests.3.0.9\lib\net40\ApprovalTests.dll</HintPath>
37+
<Private>True</Private>
38+
</Reference>
39+
<Reference Include="ApprovalUtilities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
40+
<HintPath>..\packages\ApprovalUtilities.3.0.9\lib\net35\ApprovalUtilities.dll</HintPath>
41+
<Private>True</Private>
42+
</Reference>
3543
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
3644
<SpecificVersion>False</SpecificVersion>
3745
<HintPath>..\packages\Castle.Core.3.3.0\lib\net40-client\Castle.Core.dll</HintPath>
@@ -130,6 +138,7 @@
130138
<Compile Include="ModelBinding\Pages\ModelBindingExamplePage.cs" />
131139
<Compile Include="ModelBinding\Pages\ModelFieldType.cs" />
132140
<Compile Include="ModelBinding\Pages\ModelFieldValue.cs" />
141+
<Compile Include="PartialForTests.cs" />
133142
<Compile Include="Properties\AssemblyInfo.cs" />
134143
</ItemGroup>
135144
<ItemGroup>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
Partials.cshtml
2+
3+
@model ViewModelExample
4+
5+
@{
6+
ViewBag.Title = "Partials";
7+
}
8+
9+
<h1>Partials</h1>
10+
11+
@using (var f = Html.BeginChameleonForm())
12+
{
13+
@f.Partial("_ParentPartial")
14+
@f.PartialFor(m => m.Child, "_ChildPartial")
15+
16+
using (var s = f.BeginSection("This is in the parent view"))
17+
{
18+
@s.FieldFor(m => m.Decimal).Append("in parent view")
19+
@s.Partial("_ParentPartial")
20+
@s.FieldFor(m => m.ListId).Append("in parent view")
21+
@s.PartialFor(m => m.Child, "_ChildPartial")
22+
@s.FieldFor(m => m.SomeCheckbox).Append(" in parent view")
23+
}
24+
25+
using (var n = f.BeginNavigation())
26+
{
27+
@n.Submit("Submit")
28+
}
29+
}
30+
31+
=====
32+
33+
_ParentPartial.cshtml
34+
35+
@model ViewModelExample
36+
37+
@if (this.IsInFormSection())
38+
{
39+
@:@this.FormSection().FieldFor(m => m.TextAreaField).Append("from partial against top-level model")
40+
}
41+
else
42+
{
43+
using (var s = this.Form().BeginSection("This is from a form-level partial against the top-level model")) {
44+
@s.FieldFor(m => m.Int)
45+
}
46+
}
47+
48+
=====
49+
50+
_ChildPartial.cshtml
51+
52+
@model ChildViewModel
53+
54+
@if (this.IsInFormSection())
55+
{
56+
@:@this.FormSection().FieldFor(m => m.ChildField).Append("From partial against child model")
57+
}
58+
else
59+
{
60+
using (var s = this.Form().BeginSection("This is from a form-level partial against a child model")) {
61+
@s.FieldFor(m => m.SomeEnum)
62+
}
63+
}
64+
65+
=====
66+
67+
Rendered Source
68+
69+
<form action="" method="post" novalidate="novalidate"> <fieldset>
70+
<legend>This is from a form-level partial against the top-level model</legend>
71+
<dl>
72+
<dt><label for="Int">Int</label> <em class="required">*</em></dt>
73+
<dd>
74+
<input data-val="true" data-val-number="The field Int must be a number." data-val-required="The Int field is required." id="Int" name="Int" type="text" value="0"> <span class="field-validation-valid" data-valmsg-for="Int" data-valmsg-replace="true"></span>
75+
</dd>
76+
</dl>
77+
</fieldset>
78+
<fieldset>
79+
<legend>This is from a form-level partial against a child model</legend>
80+
<dl>
81+
<dt><label for="Child_SomeEnum">Some enum</label> <em class="required">*</em></dt>
82+
<dd>
83+
<select data-val="true" data-val-required="The Some enum field is required." id="Child_SomeEnum" name="Child.SomeEnum"><option selected="selected" value="Value1">Value 1</option>
84+
<option value="ValueWithDescription">Friendly name</option>
85+
<option value="SomeOtherValue">Some other value</option>
86+
</select> <span class="field-validation-valid" data-valmsg-for="Child.SomeEnum" data-valmsg-replace="true"></span>
87+
</dd>
88+
</dl>
89+
</fieldset>
90+
<fieldset>
91+
<legend>This is in the parent view</legend>
92+
<dl>
93+
<dt><label for="Decimal">Decimal</label> <em class="required">*</em></dt>
94+
<dd>
95+
<input data-val="true" data-val-number="The field Decimal must be a number." data-val-required="The Decimal field is required." id="Decimal" name="Decimal" type="text" value="1.2300">in parent view <span class="field-validation-valid" data-valmsg-for="Decimal" data-valmsg-replace="true"></span>
96+
</dd>
97+
<dt><label for="TextAreaField">Text area field</label></dt>
98+
<dd>
99+
<textarea cols="20" id="TextAreaField" name="TextAreaField" rows="2">Initial value</textarea>from partial against top-level model <span class="field-validation-valid" data-valmsg-for="TextAreaField" data-valmsg-replace="true"></span>
100+
</dd>
101+
102+
<dt><label for="ListId">List id</label> <em class="required">*</em></dt>
103+
<dd>
104+
<select data-val="true" data-val-number="The field List id must be a number." data-val-required="The List id field is required." id="ListId" name="ListId"><option value="1">A</option>
105+
<option value="2">B</option>
106+
</select>in parent view <span class="field-validation-valid" data-valmsg-for="ListId" data-valmsg-replace="true"></span>
107+
</dd>
108+
<dt><label for="Child_ChildField">Child field</label></dt>
109+
<dd>
110+
<input id="Child_ChildField" name="Child.ChildField" type="text" value="">From partial against child model <span class="field-validation-valid" data-valmsg-for="Child.ChildField" data-valmsg-replace="true"></span>
111+
</dd>
112+
113+
<dt><label for="SomeCheckbox">Some checkbox</label> <em class="required">*</em></dt>
114+
<dd>
115+
<input data-val="true" data-val-required="The Some checkbox field is required." id="SomeCheckbox" name="SomeCheckbox" type="checkbox" value="true"> <label for="SomeCheckbox">Some checkbox</label> in parent view <span class="field-validation-valid" data-valmsg-for="SomeCheckbox" data-valmsg-replace="true"></span>
116+
</dd>
117+
</dl>
118+
</fieldset>
119+
<div class="form_navigation">
120+
<button type="submit">Submit</button> </div>
121+
</form>
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
using System;
2+
using System.CodeDom;
3+
using System.IO;
4+
using System.Net.Http;
5+
using System.Text.RegularExpressions;
6+
using ApprovalTests.Html;
7+
using ApprovalTests.Reporters;
8+
using NUnit.Framework;
9+
using OpenQA.Selenium;
10+
using OpenQA.Selenium.Support.UI;
11+
12+
namespace ChameleonForms.AcceptanceTests
13+
{
14+
/// <summary>
15+
/// Loading partial views is very difficult to test by unit testing.
16+
/// </summary>
17+
[UseReporter(typeof(DiffReporter))]
18+
public class PartialForTests
19+
{
20+
[Test]
21+
public void Should_render_correctly_when_used_via_form_or_section_and_when_used_for_top_level_property_or_sub_property()
22+
{
23+
var renderedSource = GetRenederedSource("/ExampleForms/Partials");
24+
HtmlApprovals.VerifyHtml(string.Format("Partials.cshtml\r\n\r\n{0}\r\n=====\r\n\r\n_ParentPartial.cshtml\r\n\r\n{1}\r\n=====\r\n\r\n_ChildPartial.cshtml\r\n\r\n{2}\r\n=====\r\n\r\nRendered Source\r\n\r\n{3}",
25+
GetViewContents("Partials"),
26+
GetViewContents("_ParentPartial"),
27+
GetViewContents("_ChildPartial"),
28+
renderedSource));
29+
}
30+
31+
private string GetRenederedSource(string url)
32+
{
33+
Host.Instance.Application.Browser.Navigate().GoToUrl(string.Format("http://localhost:12345{0}", url));
34+
new WebDriverWait(Host.Instance.Application.Browser, TimeSpan.FromSeconds(5))
35+
.Until(b => b.FindElement(By.Id("Int")));
36+
var renderedSource = Host.Instance.Application.Browser.PageSource;
37+
var getFormContent = new Regex(@".*?(<form(.|\n|\r)+?<\/form>).*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
38+
return getFormContent.Match(renderedSource).Groups[1].Value;
39+
}
40+
41+
private string GetViewContents(string viewPath)
42+
{
43+
return File.ReadAllText(string.Format(ViewPath, viewPath));
44+
}
45+
46+
private static readonly string ViewPath = Path.Combine(Path.GetDirectoryName(typeof(PartialForTests).Assembly.CodeBase.Replace("file:///", "")), "..", "..", "..", "ChameleonForms.Example", "Views", "ExampleForms", "{0}.cshtml");
47+
}
48+
}

ChameleonForms.AcceptanceTests/packages.config

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3+
<package id="ApprovalTests" version="3.0.9" targetFramework="net40" />
4+
<package id="ApprovalUtilities" version="3.0.9" targetFramework="net40" />
35
<package id="Castle.Core" version="3.3.0" targetFramework="net40" />
46
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
57
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />

ChameleonForms.Example/ChameleonForms.Example.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@
130130
</Compile>
131131
<Compile Include="Properties\AssemblyInfo.cs" />
132132
<Compile Include="Views\Comparison\HtmlExtensions.cs" />
133+
<Content Include="Views\ExampleForms\_ParentPartial.cshtml" />
133134
</ItemGroup>
134135
<ItemGroup>
135136
<Compile Include="App_Start\RouteConfig.cs" />
@@ -200,6 +201,8 @@
200201
<Content Include="Views\Comparison\EditorTemplates\_Layout.cshtml" />
201202
<Content Include="Views\Comparison\EditorTemplates\MembershipType.cshtml" />
202203
<Content Include="Views\Shared\_BootstrapLayout.cshtml" />
204+
<Content Include="Views\ExampleForms\Partials.cshtml" />
205+
<Content Include="Views\ExampleForms\_ChildPartial.cshtml" />
203206
</ItemGroup>
204207
<ItemGroup>
205208
<Folder Include="Models\" />

ChameleonForms.Example/Controllers/ExampleFormsController.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,17 @@ public ActionResult Buttons()
7676
{
7777
return View();
7878
}
79+
80+
public ActionResult Partials()
81+
{
82+
return View(new ViewModelExample{TextAreaField = "Initial value"});
83+
}
84+
85+
[HttpPost]
86+
public ActionResult Partials(ViewModelExample vm)
87+
{
88+
return View(vm);
89+
}
7990
}
8091

8192
public class ModelBindingViewModel
@@ -158,7 +169,7 @@ public IFieldConfiguration ModifyConfig(IFieldConfiguration config)
158169

159170

160171
// These are tested in addition to the other list tests as there
161-
// was an bug manifesting when using an array of a templated type.
172+
// was a bug manifesting when using an array of a templated type.
162173
[ReadOnly(true)]
163174
public Tuple<Int32, String>[] ChoicesAsTuples { get; set; }
164175

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
@model ViewModelExample
2+
3+
@{
4+
ViewBag.Title = "Partials";
5+
}
6+
7+
<h1>Partials</h1>
8+
9+
@using (var f = Html.BeginChameleonForm())
10+
{
11+
@f.Partial("_ParentPartial")
12+
@f.PartialFor(m => m.Child, "_ChildPartial")
13+
14+
using (var s = f.BeginSection("This is in the parent view"))
15+
{
16+
@s.FieldFor(m => m.Decimal).Append("in parent view")
17+
@s.Partial("_ParentPartial")
18+
@s.FieldFor(m => m.ListId).Append("in parent view")
19+
@s.PartialFor(m => m.Child, "_ChildPartial")
20+
@s.FieldFor(m => m.SomeCheckbox).Append(" in parent view")
21+
}
22+
23+
using (var n = f.BeginNavigation())
24+
{
25+
@n.Submit("Submit")
26+
}
27+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@model ChildViewModel
2+
3+
@if (this.IsInFormSection())
4+
{
5+
@:@this.FormSection().FieldFor(m => m.ChildField).Append("From partial against child model")
6+
}
7+
else
8+
{
9+
using (var s = this.Form().BeginSection("This is from a form-level partial against a child model")) {
10+
@s.FieldFor(m => m.SomeEnum)
11+
}
12+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
@model ViewModelExample
2+
3+
@if (this.IsInFormSection())
4+
{
5+
@:@this.FormSection().FieldFor(m => m.TextAreaField).Append("from partial against top-level model")
6+
}
7+
else
8+
{
9+
using (var s = this.Form().BeginSection("This is from a form-level partial against the top-level model")) {
10+
@s.FieldFor(m => m.Int)
11+
}
12+
}

ChameleonForms.Example/Views/Home/Index.cshtml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
<li>@Html.ActionLink("Null model with list", "NullModelWithList", "ExampleForms")</li>
2828
<li>@Html.ActionLink("Null list", "NullList", "ExampleForms")</li>
2929
<li>@Html.ActionLink("Buttons", "Buttons", "ExampleForms")</li>
30+
<li>@Html.ActionLink("Partials", "Partials", "ExampleForms")</li>
3031
</ul>
3132

3233
<h2>Random forms and UI tests</h2>

ChameleonForms.Tests/ChameleonForms.Tests.csproj

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@
3434
</PropertyGroup>
3535
<ItemGroup>
3636
<Reference Include="ApprovalTests, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
37-
<SpecificVersion>False</SpecificVersion>
38-
<HintPath>..\packages\ApprovalTests.3.0.01\lib\net40\ApprovalTests.dll</HintPath>
37+
<HintPath>..\packages\ApprovalTests.3.0.9\lib\net40\ApprovalTests.dll</HintPath>
38+
<Private>True</Private>
3939
</Reference>
4040
<Reference Include="ApprovalUtilities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
41-
<SpecificVersion>False</SpecificVersion>
42-
<HintPath>..\packages\ApprovalUtilities.3.0.01\lib\net35\ApprovalUtilities.dll</HintPath>
41+
<HintPath>..\packages\ApprovalUtilities.3.0.9\lib\net35\ApprovalUtilities.dll</HintPath>
42+
<Private>True</Private>
4343
</Reference>
4444
<Reference Include="Autofac, Version=3.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
4545
<SpecificVersion>False</SpecificVersion>

ChameleonForms.Tests/packages.config

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<packages>
3-
<package id="ApprovalTests" version="3.0.01" targetFramework="net40" />
4-
<package id="ApprovalUtilities" version="3.0.01" targetFramework="net40" />
3+
<package id="ApprovalTests" version="3.0.9" targetFramework="net40" />
4+
<package id="ApprovalUtilities" version="3.0.9" targetFramework="net40" />
55
<package id="Autofac" version="3.1.1" targetFramework="net40" />
66
<package id="AutofacContrib.NSubstitute" version="3.1.3" targetFramework="net40" />
77
<package id="Humanizer" version="1.28.0" targetFramework="net40" />

ChameleonForms/ChameleonForms.csproj

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,9 @@
9090
<Compile Include="Enums\EncType.cs" />
9191
<Compile Include="Enums\TextInputType.cs" />
9292
<Compile Include="Extensions.cs" />
93+
<Compile Include="PartialViewForm.cs" />
94+
<Compile Include="PartialViewSection.cs" />
95+
<Compile Include="ReSharper\ReSharperAnnotations.cs" />
9396
<Compile Include="Templates\TwitterBootstrap3\ButtonSize.cs" />
9497
<Compile Include="Templates\TwitterBootstrap3\EmphasisStyle.cs" />
9598
<Compile Include="FieldGenerators\Handlers\DateTimeHandler.cs" />
@@ -129,6 +132,9 @@
129132
<DependentUpon>TwitterBootstrapHtmlHelpers.cshtml</DependentUpon>
130133
</Compile>
131134
<Compile Include="Templates\TwitterBootstrap3\ButtonHtmlAttributesExtensions.cs" />
135+
<Compile Include="Utils\ExpressionExtensions.cs" />
136+
<Compile Include="Utils\SwapHtmlHelperWriter.cs" />
137+
<Compile Include="WebViewPageExtensions.cs" />
132138
</ItemGroup>
133139
<ItemGroup>
134140
<None Include="packages.config" />

ChameleonForms/Component/Field.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,7 @@ public static class FieldExtensions
8585
/// <param name="section">The section the field is being created in</param>
8686
/// <param name="property">A lamdba expression to identify the field to render the field for</param>
8787
/// <returns>A field configuration object that allows you to configure the field</returns>
88-
public static IFieldConfiguration FieldFor<TModel, T>(this Section<TModel> section, Expression<Func<TModel, T>> property)
88+
public static IFieldConfiguration FieldFor<TModel, T>(this ISection<TModel> section, Expression<Func<TModel, T>> property)
8989
{
9090
var fc = new FieldConfiguration();
9191
// ReSharper disable ObjectCreationAsStatement
@@ -108,7 +108,7 @@ public static IFieldConfiguration FieldFor<TModel, T>(this Section<TModel> secti
108108
/// <param name="property">A lamdba expression to identify the field to render the field for</param>
109109
/// <param name="config">Any configuration information for the field</param>
110110
/// <returns>The form field</returns>
111-
public static Field<TModel> BeginFieldFor<TModel, T>(this Section<TModel> section, Expression<Func<TModel, T>> property, IFieldConfiguration config = null)
111+
public static Field<TModel> BeginFieldFor<TModel, T>(this ISection<TModel> section, Expression<Func<TModel, T>> property, IFieldConfiguration config = null)
112112
{
113113
return new Field<TModel>(section.Form, true, section.Form.GetFieldGenerator(property), config);
114114
}

0 commit comments

Comments
 (0)