Is it possible to write a custom component that has several fields? This may overlap slightly with some of the issue posts that ask for templates etc.

The difference here is that I'm not asking webform to provide this out of the box via the interface, but write a custom component. Does the API support this?

The closest one of the existing components is the grid, which can have a number of sub fields radio buttons.

The use case I'm thinking about is a contact component, which has a number of text fields (and other) that never change and can be hardcoded in the component. Rather than adding fields one by one over and over, package it up and add it to a webform as just one field.

Any pointer would be awesome...

Comments

quicksketch’s picture

Yes, Webform can be extended to make any number of fields in a single component. For an example of how a component can be self-contained in a module, see Webform Pay. All the hooks necessary to create a component are documented in the webform_hooks.php file included with Webform.

NicolasH’s picture

Status: Active » Closed (fixed)

Thanks Nathan, loving the extensibility of this.

I read through all the the API docs, but it still wasn't quite clear to me where you would have the actual control of form API.

To maybe save others some time, _webform_render_component() is where you can use straight FAPI syntax, including nesting and multiple elements. It all gets processed without any fuss...

NicolasH’s picture

Status: Closed (fixed) » Active

Even though the data from the nested fields gets saved, the cid is always 0 in webform_submitted_data, rather than the actual component id.

After digging through webform and its array flattening function etc, I'm almost sure it's a nesting issue and me not declaring something properly right at the start of the form creation.

I know you don't want to deal with people's custom implementations, but if you have any pointer if this is something obvious, it'd be great.

Here's the simplified $element structure when it leaves _webform_render_component():

Array
(
    [contact] => Array
        (
            [#type] => fieldset
            [#title] => Traction

            [fname] => Array
                (
                    [#type] => textfield
                    [#title] => First Name
                    [#required] => 1
                )

            [lname] => Array
                (
                    [#type] => textfield
                    [#title] => Last Name
                    [#required] => 1
                )
        )

    [#element_validate] => Array
        (
            [0] => _webform_validate_contact
        )

    [#webform_component] => Array
        (
            [nid] => 2
            [cid] => 1
            [form_key] => contact_form
            [name] => Traction
            [type] => contact
            [value] => 
            [extra] => Array
                (
                    [title_display] => 0
                    [conditional_operator] => =
                    [description] => 
                    [unique] => 1
                    [conditional_component] => 
                    [conditional_values] => 
                )

            [mandatory] => 0
            [pid] => 0
            [weight] => 0
            [page_num] => 1
        )
)
quicksketch’s picture

You shouldn't nest elements like $form['contact']['fname'] if you don't need to, as Webform is already grouping your elements together using the 'form key' that the user has set when they create the component. What's probably happening here is that nothing is being saved to the database properly, because Webform can't handle saving multi-dimensional arrays, you're only allowed one level of values (like 'fname' and 'lname'), unless you implement _webform_submit_[component]() to flatten back out the values.

NicolasH’s picture

Whether intentional or not, this data gets actually saved in the database, I guess due to the recursiveness of _webform_client_form_submit_flatten(). The problem is that the component ID cid is not saved correctly - it ends up being always 0.

These are the contents of webform_submitted_data:

+-----+-----+-----+--------+--------------+
| nid | sid | cid | no     | data         |
+-----+-----+-----+--------+--------------+
|   2 |  13 |   0 | fname  | John         |
|   2 |  13 |   0 | lname  | Doe          |
|   2 |  13 |   2 | 0      | Some text... |
+-----+-----+-----+--------+--------------+

Note that "Some text..." is from the standard webform textfield component, with a cid of 2. The others should have a cid of 1. If I set this to 1 manually, everything else works as expected.

If I take the fieldset out of the equation:

function _webform_render_contact($component, $value = NULL, $filter = TRUE) {
  $element['fname'] = array(
    '#type' => 'textfield',
    '#title' => 'First Name',
  );

  $element['lname'] = array(
    '#type' => 'textfield',
    '#title' => 'Last Name',
  );

  $element['#webform_component'] = $component;

  return $element;
}

I then get always just the value of the last declared field, in this case the last name stored (cid also still 0):

+-----+-----+-----+----+------+
| nid | sid | cid | no | data |
+-----+-----+-----+----+------+
|   2 |   7 |   0 | 0  | Doe  |
+-----+-----+-----+----+------+

Another variation I tried was to group by form_key...also experimented with #parents, but I end up with the same result as with my fieldset - data gets stored, but cid is 0.

function _webform_render_contact($component, $value = NULL, $filter = TRUE) {
  $form_key = $component['form_key'];

  $element[$form_key]['fname'] = array(
    '#type' => 'textfield',
    '#title' => 'First Name',
    //'#parents' => array('submitted', $form_key, 'fname'),
  );

  $element[$form_key]['lname'] = array(
    '#type' => 'textfield',
    '#title' => 'Last Name',
    //'#parents' => array('submitted', $form_key, 'lname'),
  );

  $element['#webform_component'] = $component;

  return $element;
}

I'm sure I'm missing something simple yet fundamental with handling multiple fields...

NicolasH’s picture

Status: Active » Closed (fixed)

Oh noes, I just realised it was the "group" setting being switched on that made my initial version almost work (but never fully), and your suggestion not at all.

Switching it off makes it work as you described.

firebus’s picture

in addition to flattening with the _webform_submit_component hook It's also possible to manually set the #parents array on your nested fields so that FAPI flattens them for you.

for example, $element['contact']['fname'] will end up having parents ('submitted', 'contact_form', 'contact', 'fname')

if you manually set '#parents' => array('submitted', 'contact_form', 'fname') in the render hook, then webform will be happy when it tries to flatten the submission.

Anonymous’s picture

Version: 6.x-3.x-dev » 7.x-4.0-alpha4

I would like to make a custom module that integrates with webform (7.4).

I need to have a combo field which could be added to a form as one field but consists of 4 text areas.

I see your discussion above but first its confusing how @NicolasH does it, at the end he just says he figured it out, but also this is a solution for 6.3 and i need for the 7.4

Any help would be greatly appreciated.