Create a new webform element in Drupal 7

Sometimes the default field types within Webform don't work as we want them to or we want to do something beyond which will work beyond a few hacks in the theme. The following will show you what is required to define a new field type and the necessary functions to make it work.

webform-slider.png

Defining the new webform component

The first thing you need is a custom module, if you haven't got one set one up (guide on Drupal.org).

To define a new field type/component you need to use hook_webform_component_info(). This guide will go through adding a new slider component using the jQuery UI.

<?php
function MODULE_NAME_webform_component_info() {
  
$components = array();
  
$components['slider'] = array(
    
'label' => t('Slider'),
    
'description' => t('Create a slider for users to select a value.'),
    
'features' => array(
      
'csv' => TRUE,
      
'email' => TRUE,
      
'email_address' => FALSE,
      
'email_name' => FALSE,
      
'required' => FALSE,
      
'title_display' => FALSE,
      
'title_inline' => FALSE,
      
'conditional' => FALSE,
      
'group' => FALSE,
      
'spam_analysis' => FALSE,
      
'attachment' => FALSE,
    ),
    
'file' => 'components/slider.inc',
  );
  return 
$components;
}
?>

There are various elements in the featured array which should be pretty self-explanatory but I will go through them anyway.

CSV
If set to TRUE, the value of the component will be passed through to the CSV output.
Email
If set to TRUE, the value of the component will be included in the %email_values token.
Email address
If set to TRUE, the value of the component can be used as the to/from email address.
Email name
If set to TRUE, the value of the component can be used as the from email name.
Required
If set to TRUE, defines whether the component can be set as required.
Title display
If set to TRUE, the title of the field will be displayed.
Title inline
If set to TRUE and Title display is also set to TRUE, the title will be shown inline with the component.
Conditional
If set to TRUE, the component can be used as a conditional value which will change the output of other fields/components.
Group
If set to TRUE, the component can be used to group together multiple components.
Spam analysis
If set to TRUE, the component holds data that can be used for spam analysis by any modules that hook in to Webform.
Attachment
If set to TRUE, the component takes file uploads.

The file needs to be set if you plan on storing all your component related functions together in one file (which I would recommend).

Create your the file for your component.

_webform_defaults_component()

<?php
function _webform_defaults_slider() {
  return array(
    
'name' => '',
    
'form_key' => NULL,
    
'pid' => 0,
    
'weight' => 0,
    
'value' => '',
    
'mandatory' => 0,
    
'extra' => array(
      
'field_prefix' => '',
      
'field_suffix' => '',
    ),
  );
}
?>

Certain values can be left as they are, but within the extra array you have free rein to define any additional information you want saved with your component.

_webform_theme_component()

<?php
function _webform_theme_slider() {
  return array(
    
'webform_display_slider' => array(
      
'render element' => 'element',
    ),
  );
}
?>

This function allows you to add additional information in to hook_theme().

_webform_edit_component()

<?php
function _webform_edit_slider($component) {
  
$form = array();
  
$form['extra']['field_prefix'] = array(
    
'#type' => 'textfield',
    
'#title' => t('Left'),
    
'#default_value' => $component['extra']['field_prefix'],
    
'#size' => 60,
    
'#required' => TRUE,
    
'#maxlength' => 255,
    
'#weight' => 1.1,
    
'#parents' => array('extra''field_prefix'),
    
'#description' => t('Text placed to the left of the slider'),
  );
  
$form['extra']['field_suffix'] = array(
    
'#type' => 'textfield',
    
'#title' => t('Right'),
    
'#default_value' => $component['extra']['field_suffix'],
    
'#size' => 60,
    
'#required' => TRUE,
    
'#maxlength' => 255,
    
'#weight' => 1.2,
    
'#parents' => array('extra''field_suffix'),
    
'#description' => t('Text placed to the right of the slider'),
  );
  return 
$form;
}
?>

Here you define your additional form elements using the form API to add to the edit screen of the component.

_webform_render_component()

<?php
function _webform_render_slider($component$value NULL$filter TRUE) {
  
$node = isset($component['nid']) ? node_load($component['nid']) : NULL;
  
drupal_add_library('system''ui.slider');
  
drupal_add_js('jQuery(document).ready(function(){jQuery("#webform-slider-'$component['form_key'] .'").slider({
    min: 1,
    max: 5,
    step: 1,
    value: jQuery("#"+ jQuery("#webform-slider-'
$component['form_key'] .'").attr("data-rel") +" input").val(),
    slide: function(event, ui) {
      jQuery("#"+ jQuery("#webform-slider-'
$component['form_key'] .'").attr("data-rel") +" input").val(ui.value);
    }
  });});'
'inline');
  
$slider '<div class="slider-item slider-item-first">'. ($filter _webform_filter_xss($component['extra']['field_prefix']) : $component['extra']['field_prefix']) .'</div>';
  
$slider .= '<div class="slider-item slider" id="webform-slider-'$component['form_key'] .'" data-rel="webform-component-'$component['form_key'] .'"></div>';
  
$slider .= '<div class="slider-item slider-item-last">'. ($filter _webform_filter_xss($component['extra']['field_suffix']) : $component['extra']['field_suffix']) .'</div>';
  
$element = array(
    
'#type' => 'hidden',
    
'#default_value' => $filter _webform_filter_values($component['value'], $nodeNULLNULLFALSE) : $component['value'],
    
'#required' => $component['mandatory'],
    
'#weight' => $component['weight'],
    
'#prefix' => '<div class="slider-wrapper clearfix">'$slider .'</div>',
    
'#theme_wrappers' => array('webform_element'),
  );
  if (isset(
$value)) {
    
$element['#default_value'] = $value[0];
  }
  else {
    
$element['#default_value'] = 3;
  }
  return 
$element;
}
?>

The _webform_render_component() function defines how the form element is sent to the webform itself.

For the slider element, I am using a hidden field which will store the value and add some jQuery to add the slider to the form. My code is a bit of a mess and I am sure the jQuery could be written a lot better, but it gets the job done.

_webform_display_component()

<?php
function _webform_display_slider($component$value$format 'html') {
  return array(
    
'#title' => $component['extra']['field_prefix'] .' / '$component['extra']['field_suffix'],
    
'#weight' => $component['weight'],
    
'#theme' => 'webform_display_slider',
    
'#theme_wrappers' => $format == 'html' ? array('webform_element') : array('webform_element_text'),
    
'#format' => $format,
    
'#value' => $value[0],
    
'#translatable' => array('title''field_prefix''field_suffix'),
  );
}
?>

The _webform_display_component() function defines how the value is displayed from the component in the various reporting from webform. If you are using a custom #theme function that will obviously need to be defined.

<?php
function theme_webform_display_slider($variables) {
  
$element $variables['element'];
  
$value $element['#value'];
  return 
trim($value) !== '' $value ' ';
}
?>

_webform_table_component()

<?php
function _webform_table_slider($component$value) {
  return 
check_plain(empty($value[0]) ? '' $value[0]);
}
?>

This defines how the value is displayed within the reports table.

_webform_csv_headers_component() & _webform_csv_data_component()

These functions define how the component value is displayed within CSV outputs from Webform.

<?php
function _webform_csv_headers_slider($component$export_options) {
  
$header = array();
  
$header[0] = '';
  
$header[1] = $component['extra']['field_prefix'];
  
$header[2] = $component['extra']['field_suffix'];
  return 
$header;
}
?>

There are 3 rows of headers that you have the ability to enter data in to for the CSV output.

<?php
function _webform_csv_data_slider($component$export_options$value) {
  return !isset(
$value[0]) ? '' $value[0];
}
?>

For a full list of available functions look in the Webform module folder for the file called webform.api.php. This file has all the available component hooks.

Comments

Hi!
how to display the steps number above the form?

I see that you do it :)

hook_node_view(). See here for full code - http://pastebin.com/2bZbzgJu

Hie,

first thank you for all this elements.

I have a problem. The value which is inserted is all the time the default value (3 in your code) and not the selected value in the slider.

I don't understand why ? Do you have any suggestion ?

Thanks

J.Precheur

For whatever reason $value probably isn't being passed through.

This code still works with the latest version of Webform for me so you should make sure everything has been copied as is.

Can you outline the essential lines to having the custom component value saved? I have used your tutorial as a guide to create a custom select list. However, I am not getting my value saved either. As I have made a different component, I am not able to correct this by copy/pasting.

Hi James
Thanks for the guide
I am trying to create an Embeddable Webform component that contains multiple text fields (its a standard address block)
I have managed to get the fields rendering by defining in the render function but this is not correct I know as the fields are a) not saving and are not really grouped together
any idea how i link the fields together perhaps in a fieldset that can be added as one.
There is very little documentation for this on web so i guess your page is pretty popular.
thanks
s

How can I create custom element in D8 Webform?