ExpressionEngine Docs

Shared Form View

Note. You can also build out control panel views with objects using the CP form service

ExpressionEngine’s control panel markup is very modular and consistent. Because of that, we were able to abstract out the creation of most form views to a single view file to save repeating ourselves and keeping our form markup maintainable. This view is also available to add-on developers and is recommended for built-in support for form validation.

Getting started

The concept is quite familiar. In an ExpressionEngine controller file, we will create a variable and pass it to a view. The variable will be a specifically-structured array that will describe the layout and contents of our form.

Let’s get started creating a general settings form for ExpressionEngine. We’ll start with a couple text inputs, as well as some other code necessary to render the view:

// The data we'll want to populate our form fields with
$site = ee('Model')->get('Site')
    ->filter('site_id', ee()->config->item('site_id'))
    ->first();

// Form definition array
$vars['sections'] = array(
  array(
    array(
      'title' => 'site_name',
      'fields' => array(
        'site_name' => array(
          'type' => 'text',
          'value' => $site->site_label,
          'required' => TRUE
        )
      )
    ),
    // Site short name field
    array(
      'title' => 'site_short_name',
      'desc' => 'site_short_name_desc',
      'fields' => array(
        'site_short_name' => array(
          'type' => 'text',
          'value' => $site->site_name,
          'required' => TRUE
        )
      )
    )
  )
);

// Final view variables we need to render the form
$vars += array(
  'base_url' => ee('CP/URL', 'settings/general'),
  'cp_page_title' => lang('general_settings'),
  'save_btn_text' => 'btn_save_settings',
  'save_btn_text_working' => 'btn_saving'
);

We can then render the form by returning this at the end of our controller method:

return ee('View')->make('ee:_shared/form')->render($vars);

Assuming our language keys are defined above, we should end up with a form that looks like this:

Fieldset definitions

Let’s dive in closer and take a look and what makes a fieldset definition:

array(
  'title' => 'site_name',
  'desc' => 'site_name_desc',
  'fields' => array( ... )
)

This is the first level in a fieldset definition. Here are what these keys and values mean, as well as others that can be set in this dimension of the array:

Option name Description Accepted values Default value
title Name of field, required. String N/A
desc Description of field, required. String N/A
fields Array of field definitions, documented below, required. Array N/A
security Marks a setting field as potentially increasing site security, and applies the security enhance style. Boolean FALSE
caution Marks a setting field as potentially decreasing site security, and applies the security caution style. Boolean FALSE
grid Whether or not this fieldset is to have a Grid input, such as one generated by the GridInput service. The fieldset needs some extra styles and markup handling to show a Grid field. Boolean FALSE
attrs Specify any extra attributes such as classes or data attributes on the parent fieldset element of the field(s). An array can be passed in the format of array('attr-name' => 'value') and multiple attributes can be specified. Array N/A
rows For use in textareas. Number of rows that should be displayed. String N/A
cols For use in textareas. Number of columns (cols) that should be displayed. String N/A
group Specify the group name this fieldset should be included in. See Toggling field visibility for more information. String N/A

Individual field definitions

Fieldsets can contain multiple fields, and they are defined in the fields array mentioned above:

'fields' => array(
  'site_name' => array(
    'type' => 'text',
    'value' => $site->site_label,
    'required' => TRUE
  )
)

The key for each field definitions is the field’s input name. We’ll dive deeper into that array to see how we can show and customize different kinds of fields. Here are the keys available to a field definition array:

Option name Description Accepted values Default value
type Type of field, required. All field types are listed below. String name of valid field type names N/A
value Value of field to populate on page load. String (or Array when type is ‘checkbox’) N/A
required Whether or not the field is required for form submission, applies the required style. Boolean FALSE
disabled Whether or not the field input element is disabled. Boolean FALSE
choices For checkboxes, radio buttons and select fields, sets the selectable choices for that field. Array format is 'my_value' => lang('my_label'). If you need instructional text, structure your array like this:
'my_value' => [
'label' => lang('my_label'),
'instructions' => lang('my_instructions')
]
Array
disabled_choices For checkboxes, indicates options that are not currently selectable with an array of field values whose checkboxes should be disabled, e.g. ['value', 'another'] Array NULL
maxlength Sets the maxlength= attribute on text inputs. Boolean FALSE
placeholder Sets the placeholder= attribute on text inputs. String NULL
no_results For checkboxes, radio buttons and select fields, can be set to show a “no results” message and a call-to-action link button to create content that would populate options for the field. Array NULL
label Normally, the label for the field is specified in the fieldset definition, but some field types may allow a secondary label to be set such as the short-text field because it is normally paired with other short-text fields and each may need their own label. String NULL
encode For checkboxes, radio buttons and select fields, whether or not to encode the items’ display value for security. Boolean TRUE
content When type is set to html, allows for any freeform markup to be used as the field. String NULL
group_toggle If this field is to toggle the visibility of other fields, specifies the rules for that toggling. See Toggling field visibility for more information. Array N/A

Available field input types

Here are the values available to the type key documented above:

Field name Description
text Regular text input.
short-text Small text input, typically used when a fieldset needs multiple small, normally numeric, values set.
Note: Currently use of this field requires the inclusion of an option label.
textarea Textarea input.
select Select dropdown input.
dropdown A rich select dropdown .
checkbox Checkboxes displayed in a vertical list.
radio Radio buttons displayed in a vertical list.
yes_no A Toggle control that returns either y or n respectively.
file File input. Requires filepicker configuration.
NOTE: This input provides a standard file upload button allowing selection and uploading of a file from the users environment. You will need to provide your own back-end code to do something with the uploaded file. If you want to insert a standard EE File picker see notes later in this article.
image Image input. Like file but shows an image thumbnail of the selected image as well as controls to edit or remove. Requires filepicker configuration.
password Password input.
hidden Hidden input.
html Freeform HTML can be passed in via the content key in the field definition to have a custom input field.

Given what we now know about how to define field definitions and the types of fields available, let’s add a few more fields to our form:

$vars['sections'] = array(
  array(
    array(
      'title' => 'site_name',
      'fields' => array(
        'site_name' => array(
          'type' => 'text',
          'value' => $site->site_label,
          'required' => TRUE
        )
      )
    ),
    array(
      'title' => 'site_short_name',
      'desc' => 'site_short_name_desc',
      'fields' => array(
        'site_short_name' => array(
          'type' => 'text',
          'value' => $site->site_name,
          'required' => TRUE
        )
      )
    ),
    array(
      'title' => 'site_online',
      'desc' => 'site_online_desc',
      'fields' => array(
        'is_system_on' => array(
          'type' => 'inline_radio',
          'choices' => array(
            'y' => 'online',
            'n' => 'offline'
          )
        )
      )
    ),
    array(
      'title' => 'site_offline_description',
      'fields' => array(
        'site_offline_description' => array(
          'type' => 'textarea',
          'rows' => 6,
          'attrs' => 'cols="100"', // Textarea rows and cols can be added here as well
          'value' => $site->site_label,
          'required' => TRUE
        )
      )
    ),
  ),
  'date_time_settings' => array(
    array(
      'title' => 'timezone',
      'desc' => 'timezone_desc',
      'fields' => array(
        'default_site_timezone' => array(
          'type' => 'html',
          'content' => ee()->localize->timezone_menu(
            set_value('default_site_timezone') ?: ee()->config->item('default_site_timezone')
          )
        )
      )
    ),
    array(
      'title' => 'date_time_fmt',
      'desc' => 'date_time_fmt_desc',
      'fields' => array(
        'date_format' => array(
          'type' => 'select',
          'choices' => array(
            '%n/%j/%y' => 'mm/dd/yy',
            '%j-%n-%y' => 'dd-mm-yy',
            '%Y-%m-%d' => 'yyyy-mm-dd'
          )
        ),
        'time_format' => array(
          'type' => 'select',
          'choices' => array(
            '24' => lang('24_hour'),
            '12' => lang('12_hour')
          )
        )
      )
    ),
    array(
      'title' => 'include_seconds',
      'desc' => 'include_seconds_desc',
      'fields' => array(
        'include_seconds' => array('type' => 'yes_no')
      )
    )
  )
);

Notice we’ve made use of many more field types here. Also notice, we aren’t setting values on many of our new fields, that’s because we’re working with site-wide configuration settings. When a value is not specified for a field, the shared form view automatically looks in the site’s configuration for a value.

With these additions, our form should now look like this:

Our form is fully rendered and ready to write a form handler for without having to write any markup.

Adding an EE File picker to your shared view form

It is possible to add a standard EE file picker to your form. The EE File picker is used through out the EE CP system to allow access to files / images saved in the Files area, give users the option to upload additional files to the Files area, and provide a visual summary of the current content of a field via a thumbnail image.

The unfilled standard EE File picker looks like this:

When the field has an existing value it looks like this:

To add this kind of picker to your form you need to define an HTML type input and include a call to an EE internal function. The internal function is from a library that is not currently documented, so limited explanation of the parameters applicable to this function are included below.

The input definition you need to add will look something like this (assuming the file is to be held in php variable $file_field):

// ----------------------------------------
// Define an EE type File picker
// ----------------------------------------

array(
    'title' => 'EE_file_picker_example',
    'desc' => 'EE_file_picker_example_desc',
    'fields' => array(
        'file_field' => [
            'type' => 'html',
            'value' => $file_field,
            'required' => false,
            'content' => ee()->file_field->dragAndDropField('file_field', $file_field, 'all', 'image'),
        ]
    )
)

The function concerned (ee()->file_field->dragAndDropField()) has the following parameters…

dragAndDropField($field_name, $data = '', $allowed_file_dirs = 'all', $content_type = 'all')

Parameter Type Description
$field_name String The name of the field
$data String The data stored in the file field e.g. {file:XX:url} or {filedir_x}filename.ext
$allowed_file_dirs String Whether to show one upload destination or all uploade destinations
Either ‘all’ or ONE directory ID
$content_type String The content type allowed.
Either ‘all’ or ‘image’
Returns String Fully rendered file field
$file_field = ee()->file_field->dragAndDropField('file_field', $file_field, 'all', 'image');

Toggling field visibility

Sometimes you may want to toggle the visibility of certain fields based on the value of another field. A common case is selecting an option in a dropdown or radio button and having a different set of fields appear or disappear. This can be achieved automagically with form groups. Take this small example. We have a small form with a select field, then two sections of fields we want to show based on the value of the select box. Here’s how we would construct this form normally:

$vars['sections'] = array(
  array(
    array(
      'title' => 'type',
      'fields' => array(
        'type' => array(
          'type' => 'select',
          'choices' => array(
            'text' => lang('text'),
            'image' => lang('image')
          ),
          'value' => $type
        )
      )
    ),
  ),
  'text_options' => array(
    array(
      'title' => 'text',
      'fields' => array(
        'text' => array(
          'type' => 'text',
          'value' => $text
        )
      )
    ),
  ),
  'image_options' => array(
    array(
      'title' => 'image_path',
      'fields' => array(
        'image_path' => array(
          'type' => 'text',
          'value' => $image_path
        )
      )
    )
  )
);

Pretty standard form. Now we want to modify it to give it the logical groupings we want and to specify which field is going to control the toggling:

$vars['sections'] = array(
  array(
    array(
      'title' => 'type',
      'fields' => array(
        'type' => array(
          'type' => 'select',
          'choices' => array(
            'text' => lang('text'),
            'image' => lang('image')
          ),
          'group_toggle' => array(
            'text' => 'text_options',
            'image' => 'image_options'
          ),
          'value' => $type
        )
      )
    ),
  ),
  'text_options' => array(
    'group' => 'text_options',
    'settings' => array(
      array(
        'title' => 'text',
        'fields' => array(
          'text' => array(
            'type' => 'text',
            'value' => $text
          )
        )
      ),
    )
  ),
  'image_options' => array(
    'group' => 'image_options',
    'settings' => array(
      array(
        'title' => 'image_path',
        'fields' => array(
          'image_path' => array(
            'type' => 'text',
            'value' => $image_path
          )
        )
      )
    )
  )
);

Notice what’s different. We added a group_toggle key to the select’s field definition that specifies for each value of the select dropdown which group to show. Next, we needed to specify what fields are in which group. We did that by adding a group key to each section, and then nesting those field definitions under a settings key. If we have multiple fields in that section, they will all be shown/hidden based on the value of our group_toggle field. If we just want to tag specific settings to toggle and not an entire section, we can set the group key on a fieldset definition and allow all the fields to share the same section of the form:

$vars['sections'] = array(
  array(
    array(
      'title' => 'type',
      'fields' => array(
        'type' => array(
          'type' => 'select',
          'choices' => array(
            'text' => lang('text'),
            'image' => lang('image')
          ),
          'group_toggle' => array(
            'text' => 'text_options',
            'image' => 'image_options'
          ),
          'value' => $type
        )
      )
    ),
    array(
      'title' => 'text',
      'group' => 'text_options',
      'fields' => array(
        'text' => array(
          'type' => 'text',
          'value' => $text
        )
      )
    ),
    array(
      'title' => 'image_path',
      'group' => 'image_options',
      'fields' => array(
        'image_path' => array(
          'type' => 'text',
          'value' => $image_path
        )
      )
    )
  )
);

You can also include multiple groups into group parameter if the toggle needs to switch on and off several groups; those need to be separated with a pipe | delimiter. Example: ‘group’ => ‘text_options|image_options’,

Finally, we must include the JavaScript to make it all work:

ee()->cp->add_js_script(array(
  'file' => array('cp/form_group'),
));

Form validation

The shared form view is ready to take a form validation result object from the Validation Service. After you receive the result object, simply assign it the view’s variable’s array:

$vars['errors'] = $result;

As long is the field names in validation match up with the form input names, the shared form view will automatically show error messages next to their respective fields and apply the appropriate styles denoting field errors.

Tabs

The shared form view is capable of adding tabs. To do so assign a tabs variable to the view’s variable’s array:

$vars['tabs'] = $tabs;

The view expects the tabs variable to be an associative array where the key is the language key for the text of the tab and the value is the rendered HTML of the tab itself:

$tabs = array(
  'hello_world' => '<h2>Hello world!</h2>',
  'goodbye'     => '<p>What, so soon?</p>'
);

Adding forms to tabs is a matter of rendering our form data to HTML. In many cases this simply means rendering a sections array:

$permissions_tab = '';

// Assuming $sections looks like $var['sections'] as above
foreach ($sections as $name => $settings)
{
  $permissions_tab .= ee('View')->make('ee:_shared/form/section')
    ->render(array('name' => $name, 'settings' => $settings));
}

$var['tabs'] = array(
  'permissions' => $permissions_tab
);