2

I am new to drupal and currently using Drupal 8.1.8, so forgive me if the question is not in the correct format.

I have a database table with vehicles and their details which is presented to the user as sortable Tableselect table in a “vehicle update” form. When the user selects (select/update) a row in the table an Ajax call (ajaxUpdateCall) updates the relevant fields to allow the user to edit and re-submit the values.

The user can reduce the number of results in the table by entering a search phrase (search/filter) that is then used in a LIKE query, after pressing the “Search” or “Reset Search” buttons a Ajax (search_ajax_callback) call is run to update the table.

If the table is not or has not been filtered (search/filter), then the (select/update) and sorting table by the HEADER links works fine (header/sort).

The problem occurs when the table is filtered and updated with Ajax(search/filter), if I now try to sort the table (header/sort) I get an error, but (select/update) still works. The error is presented on a blank and is as follows.

[{"command":"add_css","data":"\u003Clink rel=\u0022stylesheet\u0022 href=\u0022\/sites\/fleetmanagement.dd\/files\/css\/css_eWmbbi3frMJPauCYHygIVEjcDmNqivacE1SJjDW017s.css?od92zv\u0022 media=\u0022all\u0022 \/\u003E\n\u003Clink rel=\u0022stylesheet\u0022 href=\u0022\/core\/themes\/bartik\/css\/components\/messages.css?od92zv\u0022 media=\u0022all\u0022 \/\u003E\n"},{"command":"insert","method":"replaceWith","selector":null,"data":"\n      \n    \u003Cdiv class=\u0022messages__wrapper layout-container\u0022\u003E\n          \u003Cdiv role=\u0022contentinfo\u0022 aria-label=\u0022Error message\u0022 class=\u0022messages messages--error\u0022\u003E\n          \u003Cdiv role=\u0022alert\u0022\u003E\n                  \u003Ch2 class=\u0022visually-hidden\u0022\u003EError message\u003C\/h2\u003E\n                    An unrecoverable error occurred. The uploaded file likely exceeded the maximum file size (128 MB) that this server supports.\n                \u003C\/div\u003E\n      \u003C\/div\u003E\n    \n    \u003C\/div\u003E\n  ","settings":null}]

This error seems to be related to https://www.drupal.org/node/2686005 where the table header links are “broken” after ajax. One of the suggestions was to diable the Devel Web Profiler, but this did not solve my problem.

Before (search/filter) the header links are http:// fleetmanagement.dd:8083/manage_vehicle/update?sort=desc&order=Reg%20Number and the $_GET variable contains the following:

$_GET = array(
    sort => "asc"
    order => "Reg Number");

After (search/filter) the header links are http:// fleetmanagement.dd:8083/manage_vehicle/update?ajax_form=1&_wrapper_format=drupal_ajax&sort=asc&order=Reg%20Number and the $_GET variable contains:

$_GET = array(
    ajax_form => "1"
    _wrapper_format => "drupal_ajax"
    sort => "asc"
    order => "Reg Number");

You can see that after search_ajax_callback, “ajax_form=1&_wrapper_format=drupal_ajax&” has been added to the header links and I don’t know how to edit it after the fact since I think this is the problem.

I attempted to edit $_GET to influence the creation of the header links, but this was unsuccessful.

My question is, how do you I fix my code to allow for (header/sort) after (search/filter) Ajax has been run? Or is this a core problem and I should just disable (header/sort) altogether?

Here is most of my code, starting with the form.

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Database\Database;
use Drupal\Core\Datetime\DrupalDateTime;
use Drupal\manage_vehicle\manage_vehicle_storage;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\AjaxResponse;

class vehicle_updateform extends FormBase {    
public function buildForm(array $form, FormStateInterface $form_state) {

    $database = Database::getConnection();
    $query = $database->select('a_vehicle', 'rv')
        ->extend('Drupal\Core\Database\Query\TableSortExtender')
        ->extend('Drupal\Core\Database\Query\PagerSelectExtender')//->limit(1)
        ->fields('rv', ['vehicle_id', 'registration_number', 'make', 'model', 'vin_number', 'model_year', 'start_year', 'mileage', 'capacity', 'status', 'next_service_date', 'deleted']);
    $query->condition('rv.deleted','no', '=');        

    // Attempt to change the $_GET values so that when table HEADER links are generated
    // after AJAX Filter table call, the link does not contain "ajax_form=1&_wrapper_format=drupal_ajax&"
    // Did not work.
    if (!empty($_GET['sort'])) {
        $sort = $_GET['sort'];
        $order = $_GET['order'];
        unset($_GET);
        $_GET['sort'] = $sort;
        $_GET['order'] = $order;
    }

    $UserInput = $form_state->getUserInput(); 
    if($UserInput['_triggering_element_value'] == 'Search') {
        $search_phrase = $UserInput['search']['search_phrase'];

    }else if($UserInput['_triggering_element_value'] == 'Reset Search'){ // triggering_element is the Reset Search BUTTON -> SHOW ALL
        $UserInput['search']['search_phrase'] = "";
        $form_state->setUserInput($UserInput);
        $search_phrase = "";            
    }else{
        $search_phrase = $UserInput['search']['search_phrase'];
    }

    $DB_OR = $query->orConditionGroup() 
        ->condition('registration_number', '%' . $database->escapeLike($search_phrase) . '%', 'LIKE')
        ->condition('status', '%' . $database->escapeLike($search_phrase) . '%', 'LIKE')
        ->condition('make', '%' . $database->escapeLike($search_phrase) . '%', 'LIKE')
        ->condition('model', '%' . $database->escapeLike($search_phrase) . '%', 'LIKE');
    $query->condition($DB_OR);

    $header = array(
      'vehicle_id' =>          array('data' => t('Vid'), 'field' => 'rv.vehicle_id'),
      'registration_number' => array('data' => t('Reg Number'), 'field' => 'rv.registration_number'),
      'make' =>                array('data' => t('Make'),       'field' => 'rv.make'),
      'vin_number' =>                          t('Vin Number'),
      'capacity' =>            array('data' => t('Capacity'),   'field' => 'rv.capacity'),
      'status' =>              array('data' => t('Status'),     'field' => 'rv.status',)// 'sort' => 'desc')
    );

    $result = $query
        ->orderByHeader($header)
        ->execute()
        ->fetchAll();

    $options = array(); 
    // This ensures that the pager uses the correct options after ajaxUpdateCall                
    if (empty($form_state->getCompleteForm())) {
        // Populate Table
        foreach ($result as $row) {
            $options[$row->vehicle_id] = array(
                'vehicle_id' => $row->vehicle_id,
                'registration_number' => $row->registration_number,
                'make' => $row->make,
                'vin_number' => $row->vin_number,
                'capacity' => $row->capacity,
                'status' => $row->status,
            );
            $keyed_entries[$row->vehicle_id] = $row;
        }
    }else{
        $options = $form_state->getCompleteForm();
        $options = $options['filter']['table']['results']['#options'];
    }

    $selected = $UserInput['filter']['table']['results']; // Vehicle_ID

    if (!empty($selected)){
        $selected = array('vehicle_id' => $selected);             
        $lastResult = manage_vehicle_storage::load($selected); // expect only one value to be returned            
        $default_entry = $lastResult[0];            
    }        
    $form_state->setCached(FALSE);

    $form['#tree'] = TRUE;        
    $form['actions'] = [
        '#type' => 'actions',
    ];

    $form['search'] = array(
        '#type' => 'fieldset',
        '#title' => $this->t('Search'),            
    );
    $form['search']['search_phrase'] = array(
        '#type' => 'textfield',
        '#title' => $this->t("Search for a vehicle"),
        '#size' => 8,
        '#default_value' => "",
        '#prefix' => '<div id="search_phrase-div">',
        '#suffix' => '</div>',
    );

    $form['search']['actions']['search_button'] = array(
        '#type' => 'button',
        '#value' => t('Search'),            
        '#ajax' => array(
            'callback' => '::search_ajax_callback',
            'wrapper' => 'table-div',
            'method' => 'replace',                 
        )
    );
    $form['search']['actions']['clear_button'] = array(
        '#type' => 'button',
        '#value' => t('Reset Search'),            
        '#ajax' => array(
            'callback' => '::search_ajax_callback',
            'wrapper' => 'table-div',
            'method' => 'replace',                
        )
    );

    $form['filter'] = array(
        '#type' => 'fieldset',
        '#title' => $this->t('Available Vehicles'),            
    );        
    $form['filter']['table']['results'] = array(
        '#theme' => 'table',
        //'#theme' => 'pager',
        '#type' => 'tableselect',            
        '#header' => $header,
        '#options' => $options,
        '#multiple' => FALSE, // Radios            
        '#prefix' => '<div id="table-div">',
        '#suffix' => '</div>',
        '#empty' => t('No content available.'),            
        '#submit' => array(':updateCallback:'),
        '#ajax' => array(
            'callback' => '::ajaxUpdateCall',                
        ),
    );        
    $form['filter']['table']['pager'] = [            
        '#type' => 'pager',
        '#result' => $result,
    ];

    $form['edit'] = array(
        '#type' => 'fieldset',            
        '#title' => $this->t('Selected Vehicle'),
        '#prefix' => '<div id="edit-box-div">',
        '#suffix' => '</div>',
    );        
    $form['edit']['edit_registration_number'] = array(
        '#type' => 'textfield',
        '#title' => t('registration_number'),
        '#size' => 9,
        '#maxlength' => 8,
        '#attributes' => array(
            'placeholder' => 'abc123EC',
        ),
        '#default_value' => isset($default_entry) ? $default_entry->registration_number : "",
    );
    /* The rest of the text/number/date fields that follow
    *  are the same as edit_registration_number so I left them out
    *  i.e. edit_make, edit_model, edit_vin_number, edit_mileage.....       
    */        

    $form['actions']['submit']= array(
        '#type' => 'submit',
        '#value' => $this->t('Submit Update'),            
        '#button_type' => 'primary',
    );        
    return $form;
}

Following is the Ajax callback that updates the form fields after a row in the table has been selected.

/**
 * AJAX callback handler for the vehicle_id select.
 * When a row in the table is selected, this function
 * then updates the related text fields so that they can be edited.
 */    
public function ajaxUpdateCall(array $form, FormStateInterface &$form_state){ //

    //$selected = $_POST['filter']['table']['results']; // Row ID
    $UserInput = $form_state->getUserInput();
    $selected = $UserInput['filter']['table']['results']; // Row ID

    if (!empty($selected)){             
        $rows = array('vehicle_id' => $selected);
        $lastResult = manage_vehicle_storage::load($rows);            
        $row = $lastResult[0];          

        $form['edit']['edit_registration_number']['#value'] = $row->registration_number;
        $form['edit']['edit_make']['#value'] = $row->make;
        $form['edit']['edit_model']['#value'] = $row->model;
        $form['edit']['edit_vin_number']['#value'] = $row->vin_number;
        $form['edit']['edit_mileage']['#value'] = $row->mileage;
        $form['edit']['edit_capacity']['#value'] = $row->capacity;
        $form['edit']['edit_status']['#value'] = $row->status;
        $form['edit']['edit_model_year']['#value'] = $row->model_year;
        $form['edit']['edit_start_year']['#value'] = $row->start_year;
        $form['edit']['edit_next_service_date']['#value'] = $row->next_service_date;
    }
    $response = new AjaxResponse();
    $response->addCommand(new ReplaceCommand('#edit-box-div', ($form['edit'])));

    return $response;        
} 

The last code is for the Ajax callback that updates the table based on a search phrase to limit the dispayed table options. Here is one of the places I tried to edit the $_GET variable.

/**
 * AJAX callback handler for Search phrase filtering.
 * When search phrase is entered and Search button clicked,
 * the table updates showing only relavant values.
 */
public function search_ajax_callback(array $form, FormStateInterface &$form_state){        
    $form = $form_state->getCompleteForm();
    $response = new AjaxResponse();        
    $response->addCommand(new ReplaceCommand('#table-div', ($form['filter']['table']['results'])));

    $UserInput = $form_state->getUserInput();
    $trigger = $UserInput['_triggering_element_value'];// == 'Reset Search'

    // If Reset button was clicked, then display all rows from database
    // and clear search phrase textfield.
    if($trigger == 'Reset Search'){
        $form['search']['search_phrase']['#value'] = "";                                    
        $response->addCommand(new ReplaceCommand('#search_phrase-div', ($form['search']['search_phrase'])));
    }

    // Attempt to change the $_GET values so that when table HEADER links are generated
    // after AJAX Filter table call, the link does not contain "ajax_form=1&_wrapper_format=drupal_ajax&"
    // Did not work.
    if (!empty($_GET['sort'])) {
        $sort = $_GET['sort'];
        $order = $_GET['order'];
        unset($_GET);
        $_GET['sort'] = $sort;
        $_GET['order'] = $order;
    }        
    return $response;        
}

Thanks in advance for your help!!

1 Answer 1

1

The error is caused by the url part string &ajax_form=1&_wrapper_format=drupal_ajax, which is inserted into the url by the core ajax library (ajax.js).So modifying the _GET variable will not solve the problem, but we can add a few line of custom JS code to remove that part from the url. You can add a css class "ajax-container" to your table and then use this example JS code to do the job

 $(".ajaxfix-container a").each(function(){
    var $this = $(this);
    var newurl =   this.href.replace("&ajax_form=1&_wrapper_format=drupal_ajax",'');
    $this.attr('href',newurl);
 });        

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.