FormIt is my favorite MODx addon of all time - it makes form processing actually pleasant. However, the need for the uncached [tt][[!+prefix.fieldname]][/tt] placeholders and FormItIsSelected/ FormItIsChecked has made it very hard to manage complex HTML forms.
The problem:
- Templating: The only good way to template form fields right now is to create a chunk for each field (text field, check box, etc...), but managing that many chunks is very time-consuming. Without using field chunks, you are stuck using repetative HTML that is hard to maintain: if you change the name of a field, for example, you may have to change it in the field itself, the label, value placeholder, error placeholder, label "for" and field "id", email report, help text, and possibly more.
- Performance: Outputting a simple HTML list of countries with FormItIsSelected for each one, for example, results in significant performance hits. Using chunks as described above can also cause performance hits, because you may resort to using generated placeholders such as [tt][[!+[[+prefix]].error.[[+name]]]][/tt] that need to be processed twice (or more with output filters).
[li]Email reports: If you want a custom email report, you must build it by hand every time with labels and placeholders for every field.
-
Goals:
- Use only one template chunk for all form fields (text field, select field, option field, etc...). This simplifies maintenance by allowing you to work with all form fields at once just as you normally would but without forcing you to repeat your changes for every single text field in your form. This will also allow you to maintain multiple form "styles" for all fields by simply switching the field template. Note regarding performance: the "sub-template" (e.g. text field template) is extracted from the field chunk by using a single "explode" and is temporarily cached during the lifetime of a single request using the same system splittingred uses to cache file-based chunks. This seems to keep performance about the same as using separate chunks for each field.
- Support a field wrapper template, which allows you to standardize HTML for all form fields. This is a good place to put your [tt]<label>[/tt] and surrounding [tt]<div>[/tt] or [tt]<li>[/tt] elements without having to repeat them for each field.
- Support FormIt value and error placeholders without worrying about prefixes or performance. The field template and wrapper template both have simple placeholders for [tt][[+error]][/tt], [tt][[+error_class]][/tt], and [tt][[+current_value]][/tt]. These values are generated by fetching the FormIt placeholders for the field with the prefix "fi.". If you need to change the prefix, it needs to be overriden only once for each field.
- Support infinite field types and custom field values. If you want to use a field type called autocomplete with the value connector set to "autocomplete.php", you can!
- Support groups of checkboxes, radios, selects, and other multiple or nested field groups. These use two field types from the field template: an "outer" field type (such as select, checkbox, or radio) and an "inner" field type (such as option for selects and bool for checkboxes and radios). The inner field type is automatically detected for checkbox, radio, and select, but can be specified if necessary.
- Allow overriding of outer or inner fields with your own HTML, chunks, or snippets. - Note: Snippets or parameters have not been implemented yet, so currently you’d have to use a chunk or HTML with a snippet call inside of it.
- Smart caching and performance boosts for nested fields. Since outright caching is impossible due to the changing value of "current value" or "selected" for each user and instance, a "smart caching" system is needed, mostly for fields that use multiple checkboxes, radios, and option fields, which can really affect performance. The inner HTML for these fields is cached by default, and a simple "str_replace" is used to add "selected=selected" or "checked=checked" in the right place. This vastly improves performance over the FormItIsSelected and FormItIsChecked for large sets of options, although you can always disable caching and/or use those instead if needed.
- Automatically generate label names. This is actually very simple but requires that you use a naming convention for your field names. For example, using lowercase_field_names_with_underscores can be easily made into a readable label by adding [tt][[+label:default=`[[+name:replace=`_== `:ucwords]]`]][/tt] into your template chunks. If you don’t specify a label, one will be generated for you. This may later be standardized into a simple [tt][[+label]][/tt] placeholder.
- Automatically generate email reports. Most email reports have the same format for each row, such as [tt]<b>Field Name</b>: field value[/tt]. A FormIt hook is used to create a single placeholder with all field labels and values iterated through a row template chunk. Placing this placeholder into a email report template along with the [tt][[*pagename]][/tt] or similar TV placholder allows you to use the same report template for every form.
-
The Result:
Here is an example form. It requires only two template chunks for the fields (one for the outer wrapper, and one for the various field types) and two chunks for the email report (exampleFormItEmailReport and formReportRow). A fully-commented version of this example along with all required chunks is included in the /elements/chunks/ directory.
[[!FormIt? &hooks=`spam,fiProcessArrays,fiGenerateReport,email,redirect` &emailTpl=`exampleFormItEmailReport` &emailTo=`[[++emailsender]]` &emailSubject=`New message from [[++site_name]] [[*pagetitle]] page.` &redirectTo=`6` &submitVar=`submitForm` &figrTpl=`formReportRow` &validate=`stuffgoeshere`]]
<div>[[!+fi.validation_error_message]] </div>
<form id="formExample" action="[[~[[*id]]]]" method="post">
<div>
[[!field? &name=`name`]] <!-- type defaults to text -->
[[!field? &type=`text` &req=`1` &name=`email` &tpl=`aDifferentTemplate` &outer_tpl=`ADifferentOuterTpl`]]
[[!field? &type=`hidden` &outer_tpl=`` &name=`blank`]]
[[!field? &type=`select` &default=`1` &name=`country` &label=`Your Country:` &options_chunk=`optionsCountries`]]
[[!field? &type=`textarea` &class=`elastic` &req=`1` &name=`message` &label=`Comment`]]
[[!field? &type=`radio` &req=`1` &name=`color` &label=`Your Favorite Color:` &default=`` &options=`Red==red||Blue==blue||Other==default`]]
[[!field? &type=`select` &name=`favorite_things` &multiple=`1` &array=`1` &options=`MODx||Money||Power||Other`]]
[[!field? &type=`customtype` &name=`custom_field_type` ¬e=`Make sure you add this custom field to the &tpl chunk!` &custom_placeholder=`custom_value` &another_custom_placeholder=`And another custom value` &options=`One==1||Two==2||Three==3` &option_type=`radio`]]
[[!field? &type=`submit` &name=`submitForm`]]
</div>
</form>
-
Current status:
I have had great performance boosts with selects and radios, and managing, maintaining, and templating forms has been a dream.
This component is exactly one step away from a MODx Revolution form generator. All that is needed is a wrapper snippet that grabs the values of a custom database table or template variable and uses $modx->runSnippet to run the FormIt snippet and iterate through the field snippet calls.
FormitFastPack is available on GitHub at
https://github.com/yoleg/FormitFastPack. Feel free to try it out (upload it to the root of your installation and copy and paste the snippets and chunks from the /elements/ folder) but I have only tested it on one installation (MODx 2.1) and am still improving it. After further testing on a few more sites, after I am sure I won’t be making major changes to the interface, I will release it into the repository. Feedback and suggestions are always appreciated!