I'm trying to create a custom element that consist of a few other elements, namely some select box and one timeperiod element.
I've set up custom #process
, #value_callback
, and #element_validate
and everything works well with one exception. The #element_validate` of timeperiod element is run after my own elemen's callback is which means I can't effectively use the results of that field for either validation or creation some structured output.
As far as I know it's a normal drupal7 behaviour, callbacks on forms are supposed to be run top->down. That said, however, I still need some after-submit callback on higher-level element that can access already final results of all of its children elements.
I'd also love to know why has top-down approach been chosen. Because no matter how much I think about it it's just completely non-sensical.
Specific example:
function TST_element_info() {
return array(
'station_channel' => array(
'#input' => TRUE,
'#tree' => TRUE,
'#process' => array('TST_element_process'),
'#value_callback' => 'TST_value_callback',
'#element_validate' => array('TST_element_validate'),
),
);
}
function TST_element_process($element, &$form_state, $form) {
global $user;
$opts = array(/* magic */);
$element['Sel'] = array(
'#type' => 'select',
'#options' => $opts,
'#empty_option' => t('Tsss'),
'#empty_value' => 'default',
);
...
$element['Per'] = array(
'#type' => 'timeperiod_select',
'#title' => t('Tsss:'),
'#required' => true,
'#units' => array(
'86400' => array('max' => 365, 'step size' => 1),
'3600' => array('max' => 23, 'step size' => 1),
'60' => array('max' => 55, 'step size' => 5),
),
);
return $element;
}
function station_channel_value_callback($element, $input = FALSE, &$form_state) {
if (empty($input) || $input == 'default') {
if(isset($element['#default_value'])) {
/*magic*/
} else {
return false;
}
} else {
$Sel = $input['Sel'];
$Per = $input['Per'];
}
return array(
//I'd need need to return something like (or I could do it in validate):
//But can't do it here because $input['Per'] contains raw input from its three textfields instead of computed timestamp (and even if I altered the module to do the computation in its #value_callback it would still not help because their callback is run after mine).
'path' => "weirdStuff/$Sel/period/$Per/json"
//Therefore I have to return:
'Per' => $Per,
'Sel' => $Sel,
);
}
function TST_element_validate(&$element, &$form_state) {
// Magic validation
$Sel = $element['#value']['Sel'];
$Per = $element['#value']['Per'];
$validated_input_array = array(
//Again, I'd need to do this but can't because timeperiod's element_validate is run after mine which means their $element['#value']['Per'] still contains raw data.
'path' => "weirdStuff/$Sel/period/$Per/json"
);
form_set_value($element, $validated_input_array, $form_state);
}
//It's perfectly ok in form_submit, the form_state['#values'] contains computed (timestamp) data for the period field. But that is kinda too late for my own element.
PS: Consider this example but with one of the sub-elements that are used for connection string connection being a bit more complex one that computes its true value in #element_validate.