2

I'm using this form api example: AjaxAddMore. I want to add a "remove button" for each indiviaul element, in order to remove an item but not the latest. Thouse are my changes:

...
  $form['names_fieldset'] = [
      '#type' => 'fieldset',
      '#title' => $this->t('People coming to picnic'),
      '#prefix' => '<div id="names-fieldset-wrapper">',
      '#suffix' => '</div>',
      '#attributes' => array('class'=>array('container-inline')),
    ];

    for ($i = 0; $i < $num_names; $i++) {

      $form['names_fieldset']['fields'][$i] = [
        '#type' => 'fieldset',
        '#name' => 'id_'.$i
      ];

      $form['names_fieldset']['fields'][$i]['ids'] = [
        '#type' => 'textfield',
        '#title' => $this->t('ID'),
        '#default_value' => $i,
        '#name' => 'id_'.$i
      ];

      $form['names_fieldset']['fields'][$i]['name'] = [
        '#type' => 'textfield',
        '#title' => $this->t('Name')
      ];

      $form['names_fieldset']['fields'][$i]['remove'] = [
        '#type' => 'submit',
        '#name' => 'remove_'.$i,
        '#value' => $this->t('Remove this'),
        '#submit' => ['::removethisCallback'],
        '#ajax' => [
          'callback' => '::addmoreCallback',
          'wrapper' => 'names-fieldset-wrapper',
        ],
      ];
    }
....

It works good and I can add several records. But I can't remove an item other than the latest one. This is my callback function where I've tried to unset the item I want to remove:

public function removethisCallback(array &$form, FormStateInterface $form_state) {
    $button_clicked = $form_state->getTriggeringElement()['#name'];
    $id = substr($button_clicked,7);
    $values = $form_state->getValue(['names_fieldset']);
    unset($values[$id]);
    $form_state->setValues(['names_fieldset'], array_values($values));  

    $name_field = $form_state->get('num_names');
    if ($name_field > 1) {
        $remove_button = $name_field - 1;
        $form_state->set('num_names', $remove_button);
    }   
    $form_state->setRebuild();
}

But It doesn't work. How could I remove or update an item from $form_state?

10
  • unset($values[$id]); should be unset($values[$ids]) because your $form['names_fieldset']['fields'][$i]['ids'] has an "s".
    – No Sssweat
    Apr 20, 2019 at 10:14
  • Thanks for your answer, but I don't think so, because $id is a variable that hold the item index to delete. It isn't the field 'ids'. I get the item index to delete using $form_state->getTriggerElement, and then I get #name.
    – briast
    Apr 20, 2019 at 19:08
  • Ok then you don't need to unset, you just set a new value and that will override it.
    – No Sssweat
    Apr 20, 2019 at 23:36
  • Yes. That's what I want. But I don't find a way to do it. All changes I do over $form_state are lost.
    – briast
    Apr 21, 2019 at 9:25
  • try adding a & to $form_state in your function removethisCallback(array &$form, FormStateInterface &$form_state) { or try commenting out $form_state->setRebuild(); I don't think you need this.
    – No Sssweat
    Apr 21, 2019 at 10:23

2 Answers 2

1

$form_state->setValues(['names_fieldset'], array_values($values));

setValues(array $values) only has 1 parameter

For 2 params, you need to use setValue($key, $value) without the "s"

$form_state->setValue('name', array_values($values));  

Alternatively, you could do

$values = $form_state->getValues();
$values['name'] = array_values($values);
$form_state->setValues($values);
0

I have same problem, and I think I have a working solution (D9).

public function buildForm(array $form, FormStateInterface $form_state): array {
  // $form_state contains all values. Add a new item for init.
  $values = $form_state->getValues();

  if (!isset($values['persons'])) {
    $values['persons'][] = [
      'name' => '',
      'birthdate' => '',
      'btn_text' => 'remove person',
    ];
  }
}

An example from the $form build:

foreach ($values['persons'] as $k => $v) {
  $form['persons'][$k]['name'] = [
    '#type' => 'textfield',
    '#title' => 'Person name',
  ];
}

Remove btn. #indx is the person id param.

$form['persons'][$k]['remove_person'] = [
  '#type' => 'submit',
  '#value' => 'remove person',
  '#submit' => ['::removePerson'],
  '#name' => 'reove_person_' . $k,
  '#indx' => 'indx' . $k,
  '#ajax' => [
    'callback' => '::personCallback',
    'wrapper' => 'persons',
  ],
  '#suffix' => '<hr>',
];

Remove person callback get the person index (#indx), remove the item, and rebuild the $form_state:

public function removePerson(array &$form, FormStateInterface $form_state): void {
  $values = $form_state->getValues();
  $element = $form_state->getTriggeringElement();

  if (!isset($element['#indx'])) {
    return;
  }

  $i = preg_replace('~\D~', '', $element['#indx']);

  if (isset($values['persons'][$i])) {
    unset($values['persons'][$i]);
  }

  $form_state->setValues($values);
  $form_state->setRebuild();
}

person callback rebuild the form:

public function personCallback(array &$form, FormStateInterface $form_state): array {
  return $form['persons'];
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.