The Field is a single data collection unit; you create a Field by either calling the Field
method on a Section or instantiating and outputting to the page a Field<TModel>
.
You can also create a parent field that can have child fields nested within it by instantiating a Field<TModel>
within a using
block (the start and end of the using
block will output the start and end HTML for the Field and the contents of the using
block will output the child Fields).
The Field<TModel>
class is defined as follows in the ChameleonForms.Component
namespace:
/// <summary>
/// Wraps the output of a single form field.
/// </summary>
/// <typeparam name="TModel">The view model type for the current view</typeparam>
public class Field<TModel> : FormComponent<TModel>
{
/// <summary>
/// Creates a form field.
/// </summary>
/// <param name="form">The form the field is being created in</param>
/// <param name="isParent">Whether or not the field has other fields nested within it</param>
/// <param name="fieldGenerator">A field HTML generator class</param>
/// <param name="config"> </param>
public Field(IForm<TModel> form, bool isParent, IFieldGenerator fieldGenerator, IFieldConfiguration config)
: base(form, !isParent) {...}
}
The HTML for a Field is generated via the Field
method in the template (or BeginField
and EndField
for the start and end HTML for a parent field).
A Field consists of 4 main sub-components:
- Field Element - The HTML that makes up a control that accepts data from the user
- Field Label - Text that describes a Field Element to a user (and is linked to that Field Element)
- Field Validation HTML - Markup that acts as a placeholder to display any validation messages for a particular Field Element
- Field Configuration - The configuration for a particular Field, Field Element and/or Field Label
Default usage
Manually specify HTML
If you want to define your own HTML for the Field Element, Field Label and Field Validation HTML then you can do so by using the Field
method on the Section, e.g.:
using (var s = f.BeginSection("Title")) {
@s.Field(new HtmlString("label"), new HtmlString("element")).ChainFieldConfigurationMethodsHere()
@* Or, if you want to specify all the possible values: *@
@s.Field(new HtmlString("label"), new HtmlString("element"), new HtmlString("validation"), new ModelMetadata(...), isValid: false).ChainFieldConfigurationMethodsHere()
}
The Field
method on the Section looks like this:
/// <summary>
/// Outputs a field with passed in HTML.
/// </summary>
/// <param name="labelHtml">The HTML for the label part of the field</param>
/// <param name="elementHtml">The HTML for the field element part of the field</param>
/// <param name="validationHtml">The HTML for the validation markup part of the field</param>
/// <param name="metadata">Any field metadata</param>
/// <param name="isValid">Whether or not the field is valid</param>
/// <returns>A field configuration that can be used to output the field as well as configure it fluently</returns>
public IFieldConfiguration Field(IHtmlString labelHtml, IHtmlString elementHtml, IHtmlString validationHtml = null, ModelMetadata metadata = null, bool isValid = true) {...}
Use a Field Generator to output a single field in a Section
If you would like ChameleonForms to use a Field Generator to generate the HTML for the Field Element, Field Label and Field Validation HTML from a field on the model then you can use the FieldFor
extension method on the Section, e.g.:
using (var s = f.BeginSection("Title")) {
@s.FieldFor(m => m.FieldOnTheModel).ChainFieldConfigurationMethodsHere()
}
The FieldFor
extension method looks like this:
/// <summary>
/// Creates a single form field as a child of a form section.
/// </summary>
/// <example>
/// @s.FieldFor(m => m.FirstName)
/// </example>
/// <typeparam name="TModel">The view model type for the current view</typeparam>
/// <typeparam name="T">The type of the field being generated</typeparam>
/// <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)
{
var fc = new FieldConfiguration();
new Field<TModel>(section.Form, false, section.Form.GetFieldGenerator(property), fc);
return fc;
}
Use a Field Generator to output a parent field in a Section
If you want to use a Field Generator and want to nest child Fields under a Field then you can use the BeginFieldFor
extension method on the Section (optionally with a Field Configuration), e.g.:
using (var s = f.BeginSection("Title")) {
using (var ff = s.BeginFieldFor(m => m.FieldOnTheModel, Field.Configure().ChainFieldConfigurationMethodsHere())) {
@* Child Fields *@
}
}
The BeginFieldFor
extension method looks like this:
/// <summary>
/// Creates a single form field as a child of a form section that can have other form fields nested within it.
/// </summary>
/// <example>
/// @using (var f = s.BeginFieldFor(m => m.Company)) {
/// @f.FieldFor(m => m.PositionTitle)
/// }
/// </example>
/// <typeparam name="TModel">The view model type for the current view</typeparam>
/// <typeparam name="T">The type of the field being generated</typeparam>
/// <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>
/// <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)
{
return new Field<TModel>(section.Form, true, section.Form.GetFieldGenerator(property), config);
}
Use a Field Generator to output a single field in a parent Field
If you want to use a Field Generator to create nested Fields under a parent Field then you can use the BeginFieldFor
extension method on the Field (with an optional Field Configuration), e.g.:
using (var ff = s.BeginFieldFor(m => m.FieldOnTheModel)) {
@ff.FieldFor(m => m.ChildField).ChainFieldConfigurationMethodsHere()
}
The FieldFor
extension method looks like this:
/// <summary>
/// Creates a single form field as a child of another form field.
/// </summary>
/// <example>
/// @using (var f = s.BeginFieldFor(m => m.Company)) {
/// @f.FieldFor(m => m.PositionTitle)
/// }
/// </example>
/// <typeparam name="TModel">The view model type for the current view</typeparam>
/// <typeparam name="T">The type of the field being generated</typeparam>
/// <param name="field">The parent field 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 Field<TModel> field, Expression<Func<TModel, T>> property)
{
var config = new FieldConfiguration();
new Field<TModel>(field.Form, false, field.Form.GetFieldGenerator(property), config);
return config;
}
Default HTML
Field
<dt>%labelHtml% %requiredDesignator%</dt>
<dd class="%fieldContainerClasses%">
%prependedHtml% %fieldElement% %appendedHtml% %hint% %validationHtml%
</dd>
The %requiredDesignator%
is shown if the field is required:
<em class="required">*</em>
The %hint%
is shown if a hint is specified in the Field Configuration:
<div class="hint">%hint%</div>
Begin HTML (parent)
<dt>%labelHtml% %requiredDesignator%</dt>
<dd class="%fieldContainerClasses%">
%prependedHtml% %fieldElement% %appendedHtml% %hint% %validationHtml%
<dl>
End HTML (parent)
</dl>
</dd>
Twitter Bootstrap 3 HTML
Field: Input (except checkbox and file upload), textarea or select control
<div class="form-group%if !isValid% has-error%endif%%fieldContainerClasses%">
%if withoutLabel%<span class="control-label">%endif%
%labelHtml class="control-label"%
%if withoutLabel%</span>%endif%
%if isInputGroup or isRequired%
<div class="input-group">
%endif%
%prependedHtml%
%fieldElement class="form-control"%
%appendedHtml%
%if isRequired%
<div class="input-group-addon required">%requiredDesignator%</div>
%endif%
%if isInputGroup or isRequired%
</div>
%endif%
%hint%
%validationHtml class="help-block"%
</div>
Field: Single checkbox
<div class="checkbox%if !isValid% has-error%endif%%fieldContainerClasses%">
%prependedHtml%
%fieldElement%
%requiredDesignator%
%appendedHtml%
%hint%
%validationHtml class="help-block"%
</div>
Field: Radio/Checkbox list
<div class="form-group%if !isValid% has-error%endif%%fieldContainerClasses%">
<span class="control-label">
%labelHtml% %requiredDesignator%
</span>
%prependedHtml%
%fieldElement%
%appendedHtml%
%hint%
%validationHtml class="help-block"%
</div>
Field: Other (e.g. file upload)
<div class="form-group%if !isValid% has-error%endif%%fieldContainerClasses%">
%if withoutLabel%<span class="control-label">%endif%
%labelHtml% %requiredDesignator%
%if withoutLabel%</span>%endif%
%prependedHtml%
%fieldElement%
%appendedHtml%
%hint%
%validationHtml class="help-block"%
</div>
Common elements
The %requiredDesignator%
is shown if the field is required:
<em class="required" title="Required">∗</em>
The %hint%
is shown if a hint is specified in the Field Configuration:
<div class="help-block form-hint">%hint</div>
Input Groups
If the Field Element is within an input group then the prepended and appended HTML will be wrapped in the following:
<div class="input-group-addon">%html%</div>
A field is in an input group if:
- The field is an input (except checkbox and file upload), textarea or select control and:
- The field is required (since the Form Template appends the required designator as an input group add-on); or
- You use the
AsInputGroup
extension method from theChameleonForms.Templates.TwitterBootstrap3
namespace on theIFieldConfiguration
In all other situations you will manually need to add wrapping HTML with the relevant classes (e.g. using Append
and Prepend
on the Field Configuration).
As an example of what you can do with the input group consider the following:
@s.FieldFor(m => m.Int).AsInputGroup().Append(".00").Prepend("$")
This will render like this:
In order to be able to swap out the extension method usage across your application easily if you change your form template we recommend that rather than adding a using statement to ChameleonForms.Templates.TwitterBootstrap3
for each view that has a form using the extension method you add the namespace to your Views\Web.config
file.
Parent field
Begin HTML
The HTML is the same as the Field HTML specified above, but the last <div>
is replaced with:
<div class="row nested-fields">
<div class="col-xs-1"></div>
<div class="col-xs-11">
End HTML
</div>
</div>
</div>