在 Drupal 7 中将提交输入元素更改为按钮
今天,我花了似乎永恒的时间,试图找出我在Drupal站点上创建的某种形式的东西。我正在构建一个带有上一个和下一个按钮的多步骤表单,这两个按钮都是这样的提交元素。
$form['next'] = array(
'#type' => 'submit',
'#name' => 'next',
'#submit' => array(
'mymodule_next_submit'
),
'#value' => 'Next Step',
);我需要做的是覆盖这些并使其成为按钮元素而不是输入元素。这涉及在我的模板中创建一个看起来像这样的主题函数。您也可以通过使用hook_theme_registry_alter().
/**
* Override of theme_button().
*
* Render the button element as a button and the submit element as an input element.
*/
function mytheme_button($variables) {
$element = $variables['element'];
$element['#attributes']['type'] = 'submit';
element_set_attributes($element, array('id', 'name', 'value'));
$element['#attributes']['class'][] = 'form-' . $element['#button_type'];
if (!empty($element['#attributes']['disabled'])) {
$element['#attributes']['class'][] = 'form-button-disabled';
}
if (isset($element['#buttontype']) && $element['#buttontype'] == 'button') {
$value = $element['#value'];
unset($element['#attributes']['value']);
return '';
}
else {
return '';
}
}然后我将上一个和下一个按钮定义更改为如下所示。这些元素仍然是提交表单元素,但通过使用#buttontype自定义属性将它们呈现为按钮。
$form['prev'] = array(
'#type' => 'submit',
'#name' => 'prev',
'#validate' => array(
'mymodule_previous_validate'
),
'#submit' => array(
'mymodule_previous_submit'
),
'#buttontype' => 'button',
'#value' => ' ' . t('Previous Step'),
);
$form['next'] = array(
'#type' => 'submit',
'#name' => 'next',
'#submit' => array(
'mymodule_next_submit'
),
'#buttontype' => 'button',
'#value' => t('Next Step') . ' ',
);所有这些都在表单上生成了以下输出。
这在包含单个按钮元素的表单(如多步骤过程中的第一个表单)上一切正常,但如果表单上存在另一个按钮或提交元素,则一切都会崩溃。似乎在提交表单时,它要么什么也不做,要么返回一页。也没有运行正确的验证或提交功能,所以我不得不深入研究。
经过大量挖掘并跟踪表单代码执行后,我设法弄清楚Drupal无法看到点击了哪个按钮,因此假设表单中定义的第一个按钮是正确的元素。这与我看到的行为有关,因为前一个按钮首先被执行,即使下一个按钮被点击。表单的某些页面上有“添加新项目”AJAX按钮,表单处理首先选择这些按钮,这意味着没有处理任何正确的功能。
导致这种情况的代码在form_builder()函数中。$form_state数组中的triggering_element项在表单处理验证和提交功能之前被强制为在表单上找到的第一个按钮(如果尚未设置)。
function form_builder($form_id, &$element, &$form_state) {
//...剪
if (isset($element['#type']) && $element['#type'] == 'form') {
//...剪
if (!$form_state['programmed'] && !isset($form_state['triggering_element']) && !empty($form_state['buttons'])) {
$form_state['triggering_element'] = $form_state['buttons'][0];
}
//如果触发元素指定“按钮级”验证并提交"button-level" validation and submit
//要运行的处理程序而不是默认的表单级别的处理程序,然后将它们添加到
//表单状态。
foreach (array('validate', 'submit') as $type) {
if (isset($form_state['triggering_element']['#' . $type])) {
$form_state[$type . '_handlers'] = $form_state['triggering_element']['#' . $type];
}
}
//...剪
}
//...剪
}我发现我可以通过使用表单的#after_build属性让代码以我想要的方式执行。这是在处理表单时运行的,并允许我在遇到上面的代码之前将正确的设置添加到$form_state数组。
$form['#after_build'][]='mymodule_force_triggering_element';
#after_build元素中的函数用于确定哪个按钮被点击。正确的按钮出现在$form_state数组的输入元素中,所以一个简单的逻辑位允许我为表单状态添加正确的triggering_element。
function mymodule_force_triggering_element($form, &$form_state) {
if (isset($form_state['input']['next'])) {
$form_state['triggering_element'] = $form['next'];
} elseif (isset($form_state['input']['prev'])) {
$form_state['triggering_element'] = $form['prev'];
}
return $form;
}在此之后,表单正常运行,正确的按钮运行正确的行为。我希望这有助于其他遇到相同问题的Drupal开发人员。