フォームテーブルに行を追加するボタンを実装しようとしています。ボタンのクリックごとに新しい行が追加されます。
私の問題は、AJAXコールバックでの変更が実際の$ formオブジェクトを変更していないようです:
最初のクリックで、行n°6が追加されますが、デフォルト値がないため、すでに問題となっています。次のclicsは効果がありません。コールバックの$ formパラメーターには6行目が含まれていません。
$ formを実際に変更する方法は何ですか?
これがビルドフォームです:
public function buildForm(array $form, FormStateInterface $form_state, Array $profile = NULL) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
// table
$form['myTable'] = array(
'#type' => 'table',
'#header' => array(t('COL 1'), t('COL 2'), t('COL 3')),
'#prefix' => '<div id="my-table-wrapper">',
'#suffix' => '</div>',
);
// Build the table rows and columns.
for ($cpt = 0; $cpt <= 5; $cpt++) {
// Table row
$form['myTable'][$cpt] = $this->getTableLine($cpt);
}
$form['addRow'] = array(
'#type' => 'button',
'#value' => t('Add a row'),
'#ajax' => array(
'callback' => '::ajaxAddRow',
'event' => 'click',
'wrapper' => 'my-table-wrapper',
),
);
return $form;
}
ここで、行を埋めるために呼び出される関数:
function getTableLine($key) {
$line = array();
$line['col_1'] = array(
'#type' => 'textfield',
'#default_value' => 'col 1 - row ' . $key,
);
$line['col_2'] = array(
'#type' => 'textfield',
'#default_value' => 'col 2 - row ' . $key,
);
$line['col_3'] = array(
'#type' => 'textfield',
'#default_value' => 'col 3 - row ' . $key,
);
return $line;
}
そしてAjaxコールバック:
function ajaxAddRow($form, $form_state) {
$cpt=0;
for ($x = 0; $x <= 10 & $form['myTable'][$x]; $x++) {
$cpt++;
}
// $cpt always return 6 - expected to increment
// $form['myTable'][$cpt] is always empty
$form['myTable'][$cpt] = $this->getTableLine($cpt);
return $form['myTable'];
}
詳細と警告
フォームの変更は、フォームビルダー関数(この例では、ajax_example_autocheckboxes())でのみ行う必要があります。そうしないと、検証が失敗します。コールバック関数は、フォームやその他の状態を変更してはなりません。
リファレンス: https://www.drupal.org/docs/7/api/javascript-api/ajax-forms-in-drupal-7
D7向けですが、Ajaxの仕組みの概念は変わっていません。
ビッグアイデア
ここでの大きなアイデアは次のとおりです。
- フォーム要素を操作すると、フォームが再構築されます(選択、送信など)。
- フォームビルダー関数は、その入力($ form_state)に基づいて別の方法でビルドします
- #ajax設定とコールバック関数は、新しく再構築されたフォームのすべてまたは一部を配信して、ページの一部を置換または拡張するように調整します。
これを管理するには、各クリックを検出し、フォームの非表示要素にクリックカウンターを設定します。
すべてはbuildFormで行われ、コールバック関数は単に更新するフォーム要素を返します。
public function buildForm(array $form, FormStateInterface $form_state, Array $profile = NULL) {
$form['submit'] = array(
'#type' => 'submit',
'#value' => t('Submit'),
);
// table
$form['myTable'] = array(
'#type' => 'table',
'#header' => array(t('COL 1'), t('COL 2'), t('COL 3')),
'#prefix' => '<div id="my-table-wrapper">',
'#suffix' => '</div>',
);
// Build the table rows and columns.
for ($cpt = 0; $cpt <= 5; $cpt++) {
// Table row
$form['myTable'][$cpt] = $this->getTableLine($cpt);
}
// Build the extra lines
$triggeringElement = $form_state->getTriggeringElement();
$clickCounter = 0;
// if a click occurs
if ($triggeringElement and $triggeringElement['#attributes']['id'] == 'add-row') {
// click counter is incremented
// $formstate and $form element are updated
$clickCounter=$form_state->getValue('click_counter');
$clickCounter++;
$form_state->setValue('click_counter',$clickCounter);
$form['click_counter'] = array('#type' => 'hidden', '#default_value' => 0, '#value' => $clickCounter);
} else {
$form['click_counter'] = array('#type' => 'hidden', '#default_value' => 0);
}
// Build the extra table rows and columns.
for ($k=0 ; $k<$clickCounter ; $k++) {
$form['myTable'][6+$k] = $this->getTableLine(6+$k);
}
$form['addRow'] = array(
'#type' => 'button',
'#value' => t('Add a row'),
'#ajax' => array(
'callback' => '::ajaxAddRow',
'event' => 'click',
'wrapper' => 'my-table-wrapper',
),
'#attributes' => array(
'id' => 'add-row'
),
);
return $form;
}