outline
Customizing Dynamic Form Tag Helper Generation
Since the last post, the dynamic form tag helper supports generating form groups for complex properties.
Now the html generated by the dynamic form tag helper is not customizable and it would be nice if it was.
There might be numerous reasons for customizing the default behavior, but we are going to focus on the following: styling and using custom html form controls.
In this post we are going to introduce capabilities for tweaking some default behavior and adding custom css classes by using a nested tag helper.
The full code is in the usual Github repository.
Objective
We are going to add capability for tweaking the following form elements:
- The text content of the submit button
- The css classes of the submit button
- For a specific property(form group):
- Choosing a custom
input
element razor template - Adding css classes to the generated
<label>
,<input>
or validation message<span>
- Choosing a custom
Consider the following form:
Arbitrary css classes have been applied to the BirthDate label
as well as to the “Person’s full name” input
element. This is what makes the BirthDate label
purple and the “Person’s full name”’s text yellowish green(not the real color’s name ;-)).
In addition, a custom date time picker widget has been used for the BirthDay field. Here I used the bootstrap datetimepicker which allows to select dates more conveniently.
Let’s now take a look on how to achieve this.
Customizing behavior with the TweakTagHelper
Let’s consider the usage of the dynamic tag helper in the following cshtml
view:
Here you will notice the usage of the new tweak
nested tag helper.
The TweakTagHelper
is intended to be used inside the DynamicFormTagHelper
in order to modify the generation behavior of a form group related to a specific property.
In this regard, it can accepts one of the view model’s properties as an argument along with the aspects to customize, which can be one of the following:
asp-input-path
the path to a custom html form controlasp-label-classes
,asp-input-classes
andasp-validation-classes
the additional css classes that are going to be added to the relevant form group element(label
,input
or validationspan
)
It is possible to target properties nested under complex types as we can see in the last used tweak
instance(targeting OwnedDog.BirthDate
).
The DynamicFormTagHelper
has been modified to read customization configuration and to take it in account. We are going to see later how this capability is implemented.
Customizing the Submit Button
Two attributes have been added to the DynamicFormTagHelper
:
asp-submit-button-text
for specifying the text contentasp-submit-button-classes
for specifying additional css classes for the submit button
Here is an example usage:
This sets the submit button text to “Create Person!” and adds the btn-success
class to it.
Reading Tweaking Configuration in the DynamicFormTagHelper
Consider the following snippet from the DynamicFormTagHelper
:
Under the DynamicFormTagHelper.ProcessAsync
method we create an object belonging to the TweakingConfiguration
class, we pass this object to the context
object and we launch the execution of the children tag helpers content.
The TweakingConfiguration
object acts as a data transfer object that is going to be populated by the children TweakTagHelper
.
As we can see in the following example the TweakingConfiguration
object named tweakConfig
is subsequently passed to the FormGroupBuilder.GetFormGroup
method and is also used directly to properly generate the submit button:
Transferring Data from the TweakingTagHelper to the DynamicFormTagHelper
As we saw in The Very Basics of Nesting for Tag Helpers, child to parent communication is performed via the TagHelperContext
object that is passed to each of the parent and children ProcessAsync
method.
Let’s consider the following code it’s the actual TweakingTagHelper
class implementation:
First, the HtmlTargetElement
attribute is slapped on the tag helper class to allow its usage only under the dynamic-form
tag helper.
Then, you can see that TweakTagHelper
defines the properties mapped to the relevant html attributes.
Finally, the TweakTagHelper.ProcessAsync
creates a PropertyTweakingConfiguration
object that represents the customization of a specific property. The created instance is then added to the TweakingConfiguration
object fetched from the TagHelperContext
. Keep in mind here that the fetched TweakingConfiguration
instance is the same one referenced by the dynamic-form
tag helper.
Here we can see that the TweakTagHelper
fulfills a very simple responsibility: reading data from the cshtml view and passing its parent tag helper (dynamic-form
) in a convenient format.
The implementations of the TweakingConfiguration
and the PropertyTweakingConfiguration
classes are very basic, you can take a look at them in the Github repository.
Now let’s get to the FormGroupBuilder
modifications.
Using the Tweaking Configuration inside the FormGroupBuilder
First off, the FormGroupBuilder
has been modified and became a regular class (as opposed to a static class).
As you might know the form generation process depends on the following services: ViewContext
, HtmlEncoder
, IHtmlGenerator
and recently IHtmlHelper
.
Passing the instances of these services to each method involved in the form group rendering process became painful so I refactored it to a regular class with the previous dependencies defined as private fields.
Then, a new argument tweakingConfig
argument has been added to the FormGroupBuilder.GetFormGroup
method. This argument is expected to contain the TweakingConfiguration
object built by the various tweak
tag helpers used.
The tweakingConfig
object is then passed to the private methods responsible for rendering the elements of the form group in order to allow them to take the tweaking configuration into account.
Custom Template Rendering
Let’s take a look at the private FormGroupBuilder.buildInputHtml
method:
When a tweaking configuration is available for the currently processed property
and the configuration defines a form control template via the InputTemplatePath
property, the specified template is rendered instead of the default InputTagHelper
.
The renderInputTemplate
method uses the IHtmlHelper.Editor
method to render the form control:
The previous method uses a classic IHtmlHelper
take a look at Using the old HtmlHelper inside tag helpers for more details.
Adding Custom CSS Classes
Adding custom css classes is straightforward, we just pass a TagHelperAttributeList
populated with the developer defined classes to the existing GetGeneratedContentFromTagHelper
method as in the following buildLabelHtml
method:
Notice here the usage of the C# 6 null conditional operator when passing the eventually configured classes.
Closing Thoughts
In this attempt, we showed that it was possible to introduce configurability via nested tag helpers to the dynamic-form
tag helper.
Usually developers would want to influence the form rendering via the model class and the attributes slapped on it and its properties. But it could be cleaner to specify some stuff in the cshtml view such as css classes.
A lot of work still needs to be done to make the dynamic-form
tag helper more reliable and usable, at this point it is just a prototype.
Finally, using the HtmlHelper.Editor
reminded me of the old EditorTemplates
and DisplayTemplates
facility which allowed to place display and editor templates named after a specific type in a specific location for later use via the EditorFor
and DisplayFor
methods.
EditorFor
is able to generate a form control for a specific property and is able fetch a custom editor templates by type name automatically if it is placed under Shared/EditorTemplates
.
That allowed the developer to specify a default editing widget for a specific type.
I wonder if we could bring this capability to tag helpers…