ExpressionEngine How-to Articles
Topics range from beginner to advanced, but are all born out of real world, professional, day-to-day use of ExpressionEngine. Need more information? Get training videos and ebooks on ExpressionEngine from Mijingo.
Reusable Form Templates with ProForm
by Isaac Raway
Editor Note: In this how-to article, ProForm developer Isaac Raway shows how to create reusable form templates with his ProForm module.
One of the features of ProForm is the ability to create a single template capable of rendering any form that is designed in the module. Creating a reusable form template is simple and it can provide the same consistent UI to all of your forms, reducing the amount of work it takes to create a new form.
Goals
The basic goals for this setup is to be as flexible as possible. To this end, the essential design for this system is intended to allow multiple ways of using the same base template:
- As a full form URL - this allows us to simply link to forms that have been created in ProForm, without having to hook up anything else
- As an embedded template - allows placing the form inside of another custom template that might also do other things
Setup
We will have a single template group to contain all the templates for our forms system:
forms
Within this template group, we will have these templates:
forms/index
- allows loading forms directly from a URL.forms/_layout
- contains the actual form tag and custom logic.
Follow the next sections in order to create the group and its templates now.
Template Manager
The Template Manager is where you can create and edit templates in EE:
- Log into your control panel
- Click Design > Templates > Template Manager
Create The Template Group
Once inside the Template Manager, do these steps to create the template groups:
- On the left side of the Template Manager, click New Group.
- Enter “forms” as the Template Group Name, leave the other settings blank.
- Click Submit.
Create _Layout Template
The index
template is automatically created for us.
Next, create the _layout
template:
- Click Design > Templates > Template Manager.
- On the left side of the Template Manager, click on the forms Template Group.
- On the right side of the Template Manager, click New Template to create a template within this group.
- Enter
_layout
as the Template Group Name, leave the other settings blank. - Click Submit.
Forms Index Template
Copy & paste the following code into the forms/index template.
{embed='forms/_layout' form_name='{segment_2}'}
You can use the same embed tag in other templates in order to inject the template layout into other areas of the site.
Forms Layout Template
The _layout
template actually calls our form tag, using the form_name
provided either from the index
template or wherever else we might use the embed template.
This template is based on the one that’s automatically rendered by the exp:proform:simple
tag, which you may want to use instead. However, by using the full form tag in our embed template, we can easily make changes to how each field type is rendered.
Copy & paste the following code into the forms/_layout template:
{exp:proform:head} {!-- Include the default ProForm styling. --}
{exp:proform:form form_class="proform {embed:form_name}_proform"
form_name="{embed:form_name}" variable_prefix="pf_"}
{if pf_no_results}
Invalid form name specified!
{/if}
{if pf_complete}
Thank you for your submission!
{if:else}
{pf_hidden_fields}
<input type="hidden" name="{pf_field_name}" value="{pf_field_value}" />
{/pf_hidden_fields}
<input type="hidden" name="_pf_current_step" value="{pf_current_step}" />
<input type="hidden" name="_pf_goto_step" value="" />
{if pf_multistep}
<ul class="pf_steps">
{pf_steps}
<li><a href="#{pf_step_no}" class="pf_step {pf_step_active}">{pf_step}</a></li>
{/pf_steps}
</ul>
{/if}
<div class="pf_wrap">
{pf_fieldrows}
<ul class="pf_row">
{pf_fields}
{if pf_field_type != "invisible"}
<li id="{pf_field_html_id}" class="pf_column {pf_field_html_class}">
{if pf_field_html_block}
{pf_field_html_block}
{if:elseif pf_field_heading}
<h3>{pf_field_heading}</h3>
{if:else}
{if pf_field_type != "checkbox"}
<label for="{pf_field_name}">{pf_field_label} {if pf_field_is_required}<span class="required">*</span>{/if}</label>
<div class="pf_field {if pf_vertical}pf_vertical{/if}">
{/if}
{if pf_field_type == "string"}
{if pf_field_validation:valid_email}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="validate-email {pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:else}
{if pf_field_length <= 255}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="{pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:else}
{if pf_wysiwyg}
<textarea name="{pf_field_name}" id="{pf_field_name}" class="{pf_field_is_required}">{pf_field_value}</textarea>
<script type="text/javascript">bkLib.onDomLoaded(function() { new nicEditor(pf_nic_config).panelInstance('{pf_field_name}'); });</script>
{if:else}
<textarea name="{pf_field_name}" id="{pf_field_name}" class="{pf_field_is_required}">{pf_field_value}</textarea>
{/if}
{/if}
{/if}
{if:elseif pf_field_type == "text"}
{if pf_wysiwyg}
<textarea name="{pf_field_name}" id="{pf_field_name}" class="{pf_field_is_required}">{pf_field_value}</textarea>
<script type="text/javascript">bkLib.onDomLoaded(function() { new nicEditor(pf_nic_config).panelInstance('{pf_field_name}'); });</script>
{if:else}
<textarea name="{pf_field_name}" id="{pf_field_name}" class="custom_class_here {pf_field_is_required}">{pf_field_value}</textarea>
{/if}
{if:elseif pf_field_type == "date"}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="date {pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:elseif pf_field_type == "datetime"}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="datetime {pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:elseif pf_field_type == "time"}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="time {pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:elseif pf_field_type == "integer"}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="validate-integer {pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:elseif pf_field_type == "float"}
<input type="text" name="{pf_field_name}" id="{pf_field_name}" class="validate-float {pf_field_is_required}" value="{pf_field_value}" placeholder="{pf_field_placeholder}" />
{if:elseif pf_field_type == "file"}
<div class="pf_files">
<input name="{pf_field_name}" id="{pf_field_name}" type="file" class="{pf_field_is_required}" />
</div>
{if:elseif pf_field_type == "checkbox"}
<div class="pf_field">
<div class="pf_option">
<input type="checkbox" name="{pf_field_name}" id="{pf_field_name}" value="y" {if pf_field_checked}checked="checked"{/if} class="{pf_field_is_required}" /><label for="{pf_field_name}">{pf_field_label}</label>
</div>
</div>
{if:elseif pf_field_type == "list"}
<select name="{pf_field_name}" id="{pf_field_name}" {if pf_multiple}multiple="multiple"{/if} class="{pf_field_is_required}">
{pf_field_setting_list}
<option value="{pf_key}" {pf_selected}>{pf_row}</option>
{/pf_field_setting_list}
</select>
{if pf_field_style == "checkboxes"}
{pf_field_setting_list}
<div class="pf_option {if pf_vertical}pf_vertical{/if}">
<input type="checkbox" name="{pf_field_name}" id=