Skip to content

Commit

Permalink
Merge pull request #132 from MRCollective/partialfor
Browse files Browse the repository at this point in the history
@f.PartialFor, @f.Partial, @s.PartialFor, @s.Partial
  • Loading branch information
MattDavies committed Dec 6, 2015
2 parents f0cbc32 + f1d8abe commit 0184724
Show file tree
Hide file tree
Showing 23 changed files with 662 additions and 14 deletions.
13 changes: 13 additions & 0 deletions BREAKING_CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,19 @@ ChameleonForms Breaking Changes
Version 2.0.0
=============

Deprecated `WithoutLabel` method on `IFieldConfiguration`. It still works (for now), but the method has been marked with the `[Obsolete]` attribute.

### Reason

The method has been renamed to `WithoutLabelElement` since it more closely reflects what the method does.

### Workaround

Change all instances of `WithoutLabel` to `WithoutLabelElement`.

Version 2.0.0
=============

Remove the `ReadOnlyConfiguration` public class.

### Reason
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="ApprovalTests, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
<HintPath>..\packages\ApprovalTests.3.0.9\lib\net40\ApprovalTests.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ApprovalUtilities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
<HintPath>..\packages\ApprovalUtilities.3.0.9\lib\net35\ApprovalUtilities.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Castle.Core, Version=3.3.0.0, Culture=neutral, PublicKeyToken=407dd0808d44fbdc, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\Castle.Core.3.3.0\lib\net40-client\Castle.Core.dll</HintPath>
Expand Down Expand Up @@ -130,6 +138,7 @@
<Compile Include="ModelBinding\Pages\ModelBindingExamplePage.cs" />
<Compile Include="ModelBinding\Pages\ModelFieldType.cs" />
<Compile Include="ModelBinding\Pages\ModelFieldValue.cs" />
<Compile Include="PartialForTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
Partials.cshtml

@model ViewModelExample

@{
ViewBag.Title = "Partials";
}

<h1>Partials</h1>

@using (var f = Html.BeginChameleonForm())
{
@f.Partial("_ParentPartial")
@f.PartialFor(m => m.Child, "_ChildPartial")

using (var s = f.BeginSection("This is in the parent view"))
{
@s.FieldFor(m => m.Decimal).Append("in parent view")
@s.Partial("_ParentPartial")
@s.FieldFor(m => m.ListId).Append("in parent view")
@s.PartialFor(m => m.Child, "_ChildPartial")
@s.FieldFor(m => m.SomeCheckbox).Append(" in parent view")
}

using (var n = f.BeginNavigation())
{
@n.Submit("Submit")
}
}

=====

_ParentPartial.cshtml

@model ViewModelExample

@if (this.IsInFormSection())
{
@:@this.FormSection().FieldFor(m => m.TextAreaField).Append("from partial against top-level model")
}
else
{
using (var s = this.Form().BeginSection("This is from a form-level partial against the top-level model")) {
@s.FieldFor(m => m.Int)
}
}

=====

_ChildPartial.cshtml

@model ChildViewModel

@if (this.IsInFormSection())
{
@:@this.FormSection().FieldFor(m => m.ChildField).Append("From partial against child model")
}
else
{
using (var s = this.Form().BeginSection("This is from a form-level partial against a child model")) {
@s.FieldFor(m => m.SomeEnum)
}
}

=====

Rendered Source

<form action="" method="post" novalidate="novalidate"> <fieldset>
<legend>This is from a form-level partial against the top-level model</legend>
<dl>
<dt><label for="Int">Int</label> <em class="required">*</em></dt>
<dd>
<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>
</dd>
</dl>
</fieldset>
<fieldset>
<legend>This is from a form-level partial against a child model</legend>
<dl>
<dt><label for="Child_SomeEnum">Some enum</label> <em class="required">*</em></dt>
<dd>
<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>
<option value="ValueWithDescription">Friendly name</option>
<option value="SomeOtherValue">Some other value</option>
</select> <span class="field-validation-valid" data-valmsg-for="Child.SomeEnum" data-valmsg-replace="true"></span>
</dd>
</dl>
</fieldset>
<fieldset>
<legend>This is in the parent view</legend>
<dl>
<dt><label for="Decimal">Decimal</label> <em class="required">*</em></dt>
<dd>
<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>
</dd>
<dt><label for="TextAreaField">Text area field</label></dt>
<dd>
<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>
</dd>

<dt><label for="ListId">List id</label> <em class="required">*</em></dt>
<dd>
<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>
<option value="2">B</option>
</select>in parent view <span class="field-validation-valid" data-valmsg-for="ListId" data-valmsg-replace="true"></span>
</dd>
<dt><label for="Child_ChildField">Child field</label></dt>
<dd>
<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>
</dd>

<dt><label for="SomeCheckbox">Some checkbox</label> <em class="required">*</em></dt>
<dd>
<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>
</dd>
</dl>
</fieldset>
<div class="form_navigation">
<button type="submit">Submit</button> </div>
</form>
48 changes: 48 additions & 0 deletions ChameleonForms.AcceptanceTests/PartialForTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
using System;
using System.CodeDom;
using System.IO;
using System.Net.Http;
using System.Text.RegularExpressions;
using ApprovalTests.Html;
using ApprovalTests.Reporters;
using NUnit.Framework;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;

namespace ChameleonForms.AcceptanceTests
{
/// <summary>
/// Loading partial views is very difficult to test by unit testing.
/// </summary>
[UseReporter(typeof(DiffReporter))]
public class PartialForTests
{
[Test]
public void Should_render_correctly_when_used_via_form_or_section_and_when_used_for_top_level_property_or_sub_property()
{
var renderedSource = GetRenederedSource("/ExampleForms/Partials");
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}",
GetViewContents("Partials"),
GetViewContents("_ParentPartial"),
GetViewContents("_ChildPartial"),
renderedSource));
}

private string GetRenederedSource(string url)
{
Host.Instance.Application.Browser.Navigate().GoToUrl(string.Format("http://localhost:12345{0}", url));
new WebDriverWait(Host.Instance.Application.Browser, TimeSpan.FromSeconds(5))
.Until(b => b.FindElement(By.Id("Int")));
var renderedSource = Host.Instance.Application.Browser.PageSource;
var getFormContent = new Regex(@".*?(<form(.|\n|\r)+?<\/form>).*", RegexOptions.Multiline | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant);
return getFormContent.Match(renderedSource).Groups[1].Value;
}

private string GetViewContents(string viewPath)
{
return File.ReadAllText(string.Format(ViewPath, viewPath));
}

private static readonly string ViewPath = Path.Combine(Path.GetDirectoryName(typeof(PartialForTests).Assembly.CodeBase.Replace("file:///", "")), "..", "..", "..", "ChameleonForms.Example", "Views", "ExampleForms", "{0}.cshtml");
}
}
2 changes: 2 additions & 0 deletions ChameleonForms.AcceptanceTests/packages.config
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ApprovalTests" version="3.0.9" targetFramework="net40" />
<package id="ApprovalUtilities" version="3.0.9" targetFramework="net40" />
<package id="Castle.Core" version="3.3.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
<package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
Expand Down
3 changes: 3 additions & 0 deletions ChameleonForms.Example/ChameleonForms.Example.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Views\Comparison\HtmlExtensions.cs" />
<Content Include="Views\ExampleForms\_ParentPartial.cshtml" />
</ItemGroup>
<ItemGroup>
<Compile Include="App_Start\RouteConfig.cs" />
Expand Down Expand Up @@ -200,6 +201,8 @@
<Content Include="Views\Comparison\EditorTemplates\_Layout.cshtml" />
<Content Include="Views\Comparison\EditorTemplates\MembershipType.cshtml" />
<Content Include="Views\Shared\_BootstrapLayout.cshtml" />
<Content Include="Views\ExampleForms\Partials.cshtml" />
<Content Include="Views\ExampleForms\_ChildPartial.cshtml" />
</ItemGroup>
<ItemGroup>
<Folder Include="Models\" />
Expand Down
13 changes: 12 additions & 1 deletion ChameleonForms.Example/Controllers/ExampleFormsController.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,17 @@ public ActionResult Buttons()
{
return View();
}

public ActionResult Partials()
{
return View(new ViewModelExample{TextAreaField = "Initial value"});
}

[HttpPost]
public ActionResult Partials(ViewModelExample vm)
{
return View(vm);
}
}

public class ModelBindingViewModel
Expand Down Expand Up @@ -158,7 +169,7 @@ public IFieldConfiguration ModifyConfig(IFieldConfiguration config)


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

Expand Down
27 changes: 27 additions & 0 deletions ChameleonForms.Example/Views/ExampleForms/Partials.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@model ViewModelExample

@{
ViewBag.Title = "Partials";
}

<h1>Partials</h1>

@using (var f = Html.BeginChameleonForm())
{
@f.Partial("_ParentPartial")
@f.PartialFor(m => m.Child, "_ChildPartial")

using (var s = f.BeginSection("This is in the parent view"))
{
@s.FieldFor(m => m.Decimal).Append("in parent view")
@s.Partial("_ParentPartial")
@s.FieldFor(m => m.ListId).Append("in parent view")
@s.PartialFor(m => m.Child, "_ChildPartial")
@s.FieldFor(m => m.SomeCheckbox).Append(" in parent view")
}

using (var n = f.BeginNavigation())
{
@n.Submit("Submit")
}
}
12 changes: 12 additions & 0 deletions ChameleonForms.Example/Views/ExampleForms/_ChildPartial.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@model ChildViewModel

@if (this.IsInFormSection())
{
@:@this.FormSection().FieldFor(m => m.ChildField).Append("From partial against child model")
}
else
{
using (var s = this.Form().BeginSection("This is from a form-level partial against a child model")) {
@s.FieldFor(m => m.SomeEnum)
}
}
12 changes: 12 additions & 0 deletions ChameleonForms.Example/Views/ExampleForms/_ParentPartial.cshtml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
@model ViewModelExample

@if (this.IsInFormSection())
{
@:@this.FormSection().FieldFor(m => m.TextAreaField).Append("from partial against top-level model")
}
else
{
using (var s = this.Form().BeginSection("This is from a form-level partial against the top-level model")) {
@s.FieldFor(m => m.Int)
}
}
1 change: 1 addition & 0 deletions ChameleonForms.Example/Views/Home/Index.cshtml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
<li>@Html.ActionLink("Null model with list", "NullModelWithList", "ExampleForms")</li>
<li>@Html.ActionLink("Null list", "NullList", "ExampleForms")</li>
<li>@Html.ActionLink("Buttons", "Buttons", "ExampleForms")</li>
<li>@Html.ActionLink("Partials", "Partials", "ExampleForms")</li>
</ul>

<h2>Random forms and UI tests</h2>
Expand Down
8 changes: 4 additions & 4 deletions ChameleonForms.Tests/ChameleonForms.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -34,12 +34,12 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="ApprovalTests, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ApprovalTests.3.0.01\lib\net40\ApprovalTests.dll</HintPath>
<HintPath>..\packages\ApprovalTests.3.0.9\lib\net40\ApprovalTests.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="ApprovalUtilities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=11bd7d124fc62e0f, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\packages\ApprovalUtilities.3.0.01\lib\net35\ApprovalUtilities.dll</HintPath>
<HintPath>..\packages\ApprovalUtilities.3.0.9\lib\net35\ApprovalUtilities.dll</HintPath>
<Private>True</Private>
</Reference>
<Reference Include="Autofac, Version=3.0.0.0, Culture=neutral, PublicKeyToken=17863af14b0044da, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
Expand Down
4 changes: 2 additions & 2 deletions ChameleonForms.Tests/packages.config
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="ApprovalTests" version="3.0.01" targetFramework="net40" />
<package id="ApprovalUtilities" version="3.0.01" targetFramework="net40" />
<package id="ApprovalTests" version="3.0.9" targetFramework="net40" />
<package id="ApprovalUtilities" version="3.0.9" targetFramework="net40" />
<package id="Autofac" version="3.1.1" targetFramework="net40" />
<package id="AutofacContrib.NSubstitute" version="3.1.3" targetFramework="net40" />
<package id="Humanizer" version="1.28.0" targetFramework="net40" />
Expand Down
6 changes: 6 additions & 0 deletions ChameleonForms/ChameleonForms.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,9 @@
<Compile Include="Enums\EncType.cs" />
<Compile Include="Enums\TextInputType.cs" />
<Compile Include="Extensions.cs" />
<Compile Include="PartialViewForm.cs" />
<Compile Include="PartialViewSection.cs" />
<Compile Include="ReSharper\ReSharperAnnotations.cs" />
<Compile Include="Templates\TwitterBootstrap3\ButtonSize.cs" />
<Compile Include="Templates\TwitterBootstrap3\EmphasisStyle.cs" />
<Compile Include="FieldGenerators\Handlers\DateTimeHandler.cs" />
Expand Down Expand Up @@ -129,6 +132,9 @@
<DependentUpon>TwitterBootstrapHtmlHelpers.cshtml</DependentUpon>
</Compile>
<Compile Include="Templates\TwitterBootstrap3\ButtonHtmlAttributesExtensions.cs" />
<Compile Include="Utils\ExpressionExtensions.cs" />
<Compile Include="Utils\SwapHtmlHelperWriter.cs" />
<Compile Include="WebViewPageExtensions.cs" />
</ItemGroup>
<ItemGroup>
<None Include="packages.config" />
Expand Down
4 changes: 2 additions & 2 deletions ChameleonForms/Component/Field.cs
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ public static class FieldExtensions
/// <param name="section">The section the field is being created in</param>
/// <param name="property">A lamdba expression to identify the field to render the field for</param>
/// <returns>A field configuration object that allows you to configure the field</returns>
public static IFieldConfiguration FieldFor<TModel, T>(this Section<TModel> section, Expression<Func<TModel, T>> property)
public static IFieldConfiguration FieldFor<TModel, T>(this ISection<TModel> section, Expression<Func<TModel, T>> property)
{
var fc = new FieldConfiguration();
// ReSharper disable ObjectCreationAsStatement
Expand All @@ -108,7 +108,7 @@ public static IFieldConfiguration FieldFor<TModel, T>(this Section<TModel> secti
/// <param name="property">A lamdba expression to identify the field to render the field for</param>
/// <param name="config">Any configuration information for the field</param>
/// <returns>The form field</returns>
public static Field<TModel> BeginFieldFor<TModel, T>(this Section<TModel> section, Expression<Func<TModel, T>> property, IFieldConfiguration config = null)
public static Field<TModel> BeginFieldFor<TModel, T>(this ISection<TModel> section, Expression<Func<TModel, T>> property, IFieldConfiguration config = null)
{
return new Field<TModel>(section.Form, true, section.Form.GetFieldGenerator(property), config);
}
Expand Down
Loading

0 comments on commit 0184724

Please sign in to comment.