I'm learning drupal 8. I want create a page who contain a 'two dimensionnal' 'add another item' form. My code works well almost, but I have a strange behavior when I add rooms to a house (there is a strange value in my debug logs from the FormStateInterface::getTriggeringElement(), see to the bottom for the code and log)
First : I have two structures, houses and rooms. The user can create some houses and for each house, he can create some rooms :
When I add some houses, the form works fine :
When I add some rooms to the last house, the form works fine too :
But when I add some rooms to any "no-last" house, the form doesn't work fine (in the screenshot, I click one time to the "add room" in the block house '1', the label of the "house 1" became "house 2" (?!) and on click add 5 rooms (?!) :
Here my code and a strange debug log, I don't explain why I get this value (from the getTriggeringElement() in the room_addMoreSubmit callback and this is the problem I think)
<?php
namespace Drupal\projet\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
class HouseForm extends FormBase {
public function getFormId(){
return 'custom_rooms_form';
}
function buildForm(array $form, FormStateInterface $form_state) {
$house_count = $form_state->get('house_count');
if (is_null($house_count)) {
$house_count = 1;
$form_state->set('house_count', $house_count);
}
$form['house'] = array(
//'#tree' => TRUE,
'#prefix' => '<div id="house-replace">',
'#suffix' => '</div>'
);
for ($house_delta = 0; $house_delta < $house_count; $house_delta++) {
if (!isset($form['house'][$house_delta])) {
$room_count[$house_delta] = $form_state->get('room_count_'.$house_delta);
if (is_null($room_count[$house_delta])) {
$room_count[$house_delta] = 1;
$form_state->set('room_count_'.$house_delta, $room_count[$house_delta]);
}
dd($room_count, "room_COUNT");
$form['house'][$house_delta]['room'] = array(
'#type' => 'fieldset',
'#title' => t('house : '.$house_delta),
//'#tree' => TRUE,
'#prefix' => '<div id="room-replace-'.$house_delta.'">',
'#suffix' => '</div>'
);
for ($room_delta = 0; $room_delta < $room_count[$house_delta]; $room_delta++) {
if (!isset($form['house'][$house_delta]['room'][$room_delta])) {
$room = array(
'#type' => 'textfield'
);
$check = array(
'#type' => 'checkbox'
);
$form['house'][$house_delta]['room'][$room_delta] = array(
'#type' => 'fieldset',
'#title' => t('room : '.$house_delta.'.'.$room_delta),
);
$form['house'][$house_delta]['room'][$room_delta]['text'] = $room;
$form['house'][$house_delta]['room'][$room_delta]['check'] = $check;
}
}
$form['house'][$house_delta]['room']['add'] = array(
'#type' => 'submit',
'#name' => 'add',
'#value' => t('Add room'),
'#attributes' => array('class' => array('field-add-more-submit'), 'house_delta' => array($house_delta)),
'#submit' => array(array(get_class($this), 'room_addMoreSubmit')),
'#ajax' => array(
'callback' => array($this, 'room_addMoreCallback'),
'wrapper' => 'room-replace-'.$house_delta,
'effect' => 'fade',
),
);
}
}
$form['house']['add'] = array(
'#type' => 'submit',
'#name' => 'add',
'#value' => t('Add house'),
'#attributes' => array('class' => array('field-add-more-submit')),
'#submit' => array(array(get_class($this), 'house_addMoreSubmit')),
'#ajax' => array(
'callback' => array($this, 'house_addMoreCallback'),
'wrapper' => 'house-replace',
'effect' => 'fade',
),
);
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Create'),
);
return $form;
}
public function room_addMoreSubmit(array $form, FormStateInterface $form_state) {
dd($form_state->getTriggeringElement(), "room : getTriggeringElement()"); // below, the log when I add a room to the house '1' (result see above with the last screenshot: "the house 1" became "house 2" and one click add 5 rooms)
$house = $form_state->getTriggeringElement()["#array_parents"][1];
$c = $form_state->get('room_count_'.$house) + 1;
$form_state->set('room_count_'.$house, $c);
$form_state->setRebuild(TRUE);
}
public function room_addMoreCallback(array $form, FormStateInterface $form_state) {
$house = $form_state->getTriggeringElement()["#array_parents"][1];
return $form['house'][$house]['room'];
}
public function house_addMoreSubmit(array $form, FormStateInterface $form_state) {
dd($form_state->getTriggeringElement()["#array_parents"], "house : getTriggeringElement()");
$c = $form_state->get('house_count') + 1;
$form_state->set('house_count', $c);
$form_state->setRebuild(TRUE);
}
public function house_addMoreCallback(array $form, FormStateInterface $form_state) {
return $form['house'];
}
}
The log ('dd' in the room_addMoreSubmit) when I click on the "add room" button in the house "1":
When I click on the "add room" button in the house number 1, getTriggeringElement return the array parents of the add button. And, as you can see, the parent is "2" not "1" (the house 1) So when I click on the "add room" button of the house 1, this is the house "2" which is identified and not the house "1".
I don't understand why...Use the getTriggeringElement is not the good way ?
The solution :
$form['house'][$house_delta]['room']['add'] = array(
'#type' => 'submit',
'#name' => 'add-'.$house_delta,
'#value' => t('Add room'),
'#attributes' => array('class' => array('field-add-more-submit'), 'house_delta' => array($house_delta)),
'#submit' => array(array(get_class($this), 'room_addMoreSubmit')),
'#ajax' => array(
'callback' => array($this, 'room_addMoreCallback'),
'wrapper' => 'room-replace-'.$house_delta,
'effect' => 'fade',
),
);
The unique name was the issue of my problem. So, I change the name attribute.