Добрый вечер!
Подскажите пожалуйста, как изменить значение поля в форме, в зависимости от выбранного варианта в выпадающем списке используя Ajax. Как, например, изменить значение Title на значение из выпадающего списка.
Имею вот такой form_alter:
<?phpfunction module_form_node_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
$form['fruits'] = array(
'#type' => 'select',
'#title' => 'Выберите фрукт',
'#options' => [
1 => 'Киви',
2 => 'Апельсин',
3 => 'Банан',
4 => 'Ананас',
],
'#empty_option' => t('- Выберите фрукт -'),
'#ajax' => array(
'callback' => 'SelectAjax',
'wrapper' => 'model_wrapper',
),
);
$form['title']['widget'][0]['value']['#default_value'] = $get_fruits;
}
function SelectAjax(array &$form, FormStateInterface $form_state) {
$get_fruits = $form_state->getValue('fruits');
$form['title']['widget'][0]['value']['#default_value'] = $get_fruits;
$form_state->setRebuild(true);
return $form['title'];
}
?>
Насколько я понял Callback не возвращает значение.
Подскажите пожалуйста, как исправить.
Заранее благодарю!
Комментарии
https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Form%21Fo...
Выставлять надо не дефолтное значение, а инпут.
Хотя вообще лучше зависимое поле делать нередактируемым, например задать ему тип value, а текущее значение выводить через markup.
Ваш вариант хорош, если нужна возможность редактировать подставленное значение.
Используйте AJAX-команды.
Если при изменении элементов форм возвращать не кусок формы, а аякс-команду, то можно словить очень много граблей. Например, форма отданная аяксовой командой, перестанет быть аяксовой, если принудительно не обновить drupalSettings.
by https://www.drupal.org/docs/8/api/javascript-api/ajax-forms
Я как раз имел в виду именно такой пример. Если на возвращаемом элементе должен висеть аякс, то после первого обновления аякс с элемента отвалится, т.к. айдишник обновится, а drupalSettings завязаны на старый айдишник. Именно это произойдет, если применить код из того примера, т.к. SettingsCommand там в респонс не добавлена. И ещё прикол, как получить сам этот сеттингс - об этом вообще нигде не написано, это я помню каким-то чудом догадался, что renderRoot добавляет сеттинги в исходный рендер-массив.
Как бы то ни было, решать задачу автора через аякс-команды вообще нет никакого смысла. Возвращать в коллбэке форму или её кусок - это наиболее предпочтительный вариант для form api (о том, что он предпочтительный, не пишут в доках, об этом узнаёшь после десятков часов потраченных на отладку форм с иксдебагом). Использовать аякс-команды в сочетании с form api нужно только в том случае, если по аяксу нужно обновить несколько обёрток, либо какую-то обёртку за пределами формы.
ID'шник же устанавливается вручную, в примере. Не отвалится.
Но, я вероятно не правильно понял задачу, считал что нужно менять тайтл страницы, а он не в форме. Если тайтл - элемент формы, а именно - текстовое поле, нужно просто установить "#value / #default_value" у нужного элемента и вернуть его.
В приведенным ТС'ом коде, не уверен конечно, но вероятно - указана неправильная обертка элемента ('wrapper' => 'model_wrapper'). Полагаю, потому и "не срабатывает" коллбек.
Нужно или установить элементу этот ID'шник, или изменить значение ID'шника на то, что у элемента.
Хорошая практика https://www.drupal.org/project/examples -> AJAX Example
Эти же примеры я и использую. Только там нет примеров работы с полями сущностей.
Дело в том, что с кастомными полями, ajax работает как положено, значения полей в таком случае можно заменить!
Например такой код для кастомных полей работает отлично, callback срабатывает:
<?phpfunction module_form_node_place_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
$form['fruits'] = array(
'#type' => 'select',
'#title' => 'Выберите фрукт',
'#options' => [
1 => 'Киви',
2 => 'Апельсин',
3 => 'Банан',
4 => 'Ананас',
],
'#empty_option' => t('- Выберите фрукт -'),
'#ajax' => array(
'callback' => 'SelectAjax',
'wrapper' => 'model_wrapper',
),
);
$form['title_field'] = array(
'#type' => 'textfield',
'#title' => t('textfield'),
'#value' => "",
'#prefix' => '<div id="model_wrapper">',
'#suffix' => '</div>',
);
}
function SelectAjax(array &$form, FormStateInterface $form_state) {
$get_fruits = $form_state->getValue('fruits');
$form['title_field']['#value'] = $get_fruits;
$form_state->setRebuild(true);
return $form['title_field'];
}?>
Насколько я понял, дело в wrappere.
Как добавить (заменить) нужный wrapper для полей сущности, например поля 'Title' вида:
$form['title']['widget'][0]['value']['#default_value'];
Просмотрел через kint виджеты полей сущности, они расположены в контейнерах и имеют большой уровень вложенности и классов, как применить к ним wrapper?
Может тогда callback сработает как положено.
Вообще я хочу добиться следующего функционала:
при выборе пользователем значения из выпадающего списка, устанавливать его в качестве значения термина таксономии в форме добавления/редактирования нод.
Значения эти имеют id и имена уже созданных терминов таксономии и приходят в виде массива, который устанавливается в качестве '#options' выпадающего списка.
Массив в свое время подвергается фильтрации на основе прав доступа пользователей.
Всё это уже реализовано, массив обработан и установлен в качестве '#options'!!!
ID выбранного в выпадающем списке термина таксономии, успешно подставляется через ajax в любое кастомное поле, но только не в '#default_value' виджетов "базовых" полей сущностей.
По большому счёту виджет поля ввода терминов таксономии в форме мне не нужен и его можно скрыть или удалить через UNSET, но как указать tid выбранного термина таксономии из моего кастомного поля при сохранении ноды!?
Может есть другой вариант реализации данного функционала?
Заранее благодарю за советы!
Вам всё это вообще не надо. Создайте представление entity reference и повесьте его на поле в настройках отображения формы. Этот функционал есть в ядре, не нужен ни кастомный код, ни сторонние модули.
Почему Вы решили не использовать видгет entity_reference ?
Entity reference я пробовал, но он мне не подходит т.к. я использую два зависимых(!!!) выпадающих списка.
В первом выпадающем списке, в качестве выбора доступны только родительские термины таксономии.
После выбора родительского термина, через Ajax загружается второй выпадающий список, в котором для выбора пользователю доступны дочерние термины выбранного выше родительского термина.
Более того, массив этих дочерних терминов таксономии, подвергается определённой фильтрации на основе прав доступа пользователя. То есть определенному пользователю для выбора доступны не все термины таксономии.
Повторюсь - всё это уже реализовано!
Из второго зависимого выпадающего списка через Ajax приходит тот самый ID нужного дочернего термина, но как именно установить его в качестве значения базового поля и/или указать это значение (TID) при сохранении нужной нам ноды???
P.S. при использовании Entity reference разве можно реализовать зависимые выпадающие списки с выбором сначала родительского а после дочернего термина?
Я смог добиться в Entity reference показа в выпадающем списке только или всех терминов или только родительских или дочерних терминов.
https://www.drupal.org/project/shs
https://www.drupal.org/project/cshs
Про иерархические списки изначально не было речи. Выше правильно посоветовали - shs, но он не будет фильтровать по правам доступа. В вашем случае можно воспользоваться этим модулем и в hook_form_alter удалять лишние опции. Но в форме сущностей через альтер не очень удобно работать из-за огромной вложенности элементов, которая к тому же может постоянно меняться. Куда предпочтительнее было бы сделать свой виджет для поля. Можно даже сделать виджет, унаследовавшись от того же shs, просто переопределив пару методов, добавив туда фильтрацию списков.
Вот что нашёл: https://api.drupal.org/api/drupal/core%21modules%21options%21options.api...
Этот хук также вызывается и в shs. То есть ставим shs и пишем один хук в своём модуле.
hook_options_list_alter - действительно интересный хук, сейчас буду его пробовать и shs попутно "поковыряю".
О результатах отпишусь!
Спасибо всем отписавшимся!