Expected Output:

  1. User triggers remove item button.
  2. Program updates database.
  3. Form gets rebuild with updated information from the database.
  4. AJAX replaces the old information with new information from buildForm.

Actual Output: The expected output only occurs on the first iteration. If the user triggers remove item button on another item. The database will get updated. But the AJAX is not executed.

I'm still learning the Drupal AJAX system, I don't understand why this works for only one iteration.


//get information from shopping cart table
        $cart = new \Drupal\pps_shopping_cart\Controller\ShoppingCart();
        $items = $cart->getShoppingCartItems();

        //display review order
        if($step === 1)
            //generate table structure
            $form['table'] = [
                '#type' => 'table',
                '#header' => [
                    $this->t("Manufacturer Part Number"),
                    $this->t("Order QTY"),
                    $this->t("Unit Price"),
                '#empty' => $this->t("You don't have any items in your cart.")

            $index = 0;
            foreach($items as $record)
                $imgURL = $cart->getImageUrlByPartNumber($record->pno);
                $form['table'][$index]['image'] = [
                    '#type' => 'container',
                    '#markup' => "<img src='{$imgURL}' alt='{$record->pno}' width='200px' height='200px'>"

                $form['table'][$index]['pno'] = [
                    '#type' => 'label',
                    '#title' => $record->pno

                $form['table'][$index]['pno']['reference'] = [
                    '#type' => 'hidden',
                    '#value' => $record->pno

                $form['table'][$index]['qty'] = [
                    '#type' => 'number',
                    '#default_value' => $record->qty,
                    '#min' => 0,
                    '#max' => $cart->getCustomerAvlQty($record->pno)

                $form['table'][$index]['price'] = [
                    '#type' => 'label',
                    '#title' => '$' . number_format($record->price, 2)

                $form['table'][$index]['remove'] = [
                    '#type' => 'submit',
                    '#name' => $record->pno,
                    '#value' => $this->t("Remove"),
                    '#validate' => ['::validateRemoveItem'],
                    '#submit' => ['::submitRemoveItem'],
                    '#ajax' => [
                        'callback' => [$this, 'ajaxRemoveItem'],
                        'wrapper' => 'edit-table'


            if($index > 0)
                $form['actions'] = ['#type' => 'actions'];
                $form['actions']['review'] = [
                    '#type' => 'submit',
                    '#value' => $this->t("Review Order"),
                    '#validate' => ['::validateReview'],
                    '#submit' => ['::submitReview']

                $form['actions']['save'] = [
                    '#type' => 'submit',
                    '#value' => $this->t("Save"),
                    '#validate' => ['::validateSaveOrder'],
                    '#submit' => ['::submitSaveOrder'],
                    '#ajax' => ['callback' => [$this, 'ajaxSaveOrder']]


 $cart = new \Drupal\pps_shopping_cart\Controller\ShoppingCart();



        $response = new AjaxResponse();
        $response->addCommand(new ReplaceCommand('#edit-table', $form['table']));
        $response->addCommand(new OpenModalDialogCommand($this->t("Removing Item from Cart"), $this->t("@item: has been removed from your shopping cart!", ['@item' => $form_state->getTriggeringElement()['#name']])));
        return $response;
  • I'm going to take a wild guess that "'#edit-table'" doesn't exist when the element is generated again and is probably "'#edit-table-1", so you will need a more static selector.
    – Kevin
    Nov 22, 2018 at 17:41
  • @Kevin yes! that is exactly what is happening. The problem is solved by explicitly defining the id attribute. Thanks for the advice!
    – Devin
    Nov 22, 2018 at 18:28

2 Answers 2


I'm missing something like

$form['table']['#attributes']['id'] = 'edit-table';

in your code. You are telling Drupal's Ajax to replace the DOM element with id edit-table with the HTML output of $form['table']. In the 2nd iteration it can't find the DOM element to replace anymore, so nothing happens


Drupal rewrites the form element IDs when it rebuilds the form so you have to explicitly set the form ID in the returned form element.


"The returned array replaces the element with the ID specified by the wrapper element (#edit-output). This is important to remember when deciding what to return from the callback! Whatever was specified by the given ID is completely replaced by the new HTML! Make sure that the returned field also contains the id #edit-output or the next AJAX callback will not find a target to place the rendered code into."

Your Answer

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

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