paging – Paginated Ajax form with non-SQL data

With Drupal 8 I have created a form to load data from a web service with Ajax below the form. Now I'm trying to add pagination to the result, but the paginator was not loading, am I doing something wrong? I've tried to follow the guide from https://www.drupal.org/docs/7/creating-custom-modules/howtos/paging-non-sql-data, but it's for Drupal 7. Thanks in advance ,

Here is my code:

$ response contains an array of data from the web service.

$ total contains the record of the results

//number of records per page
$num_per_page = 3;
//get current page
$current_page = pager_default_initialize($total, $num_per_page);
$chunks = array_chunk($response, $num_per_page);
$current_page_items = $chunks($current_page);
foreach ($current_page_items as $i => $item) {
  $current_page_items($i) = array(
    '#theme' => 'ajax_search',
    '#item' => $item,
  );
}

// Generate the render array for our page
$render_array('page') = array (
  '#theme' => 'item_list',
  '#items' => $current_page_items,
);
// Calls Drupal standard pager theme and set 5 page links on pager
$render_array('pager') = array (
  '#theme' => 'pager',
  '#quantity' => 5,
);

$response = new AjaxResponse();
$response->addCommand(
  new HtmlCommand(
    '.result_message',
    $render_array)
  );
return $response;

8 – Paginated form with Ajax check box triggers: The specified # ajax callback is empty or unavailable

Using Drupal 8.7.7 I have a form that consists of a paginated user table.

Each user line contains a checkbox in the form to which an Ajax event is attached. Essentially, this allows me to save the selection in the user's session as they select users and switch to other pages.

The form works properly on the first page. However, if you switch to the second page, only the first selection works. Each selection made afterwards triggers the error:

Symfony Component HttpKernel Exception HttpException: The specified # ajax callback is empty or can not be called. in Drupal Core Form FormAjaxResponseBuilder-> buildResponse () (line 67 of /var/www/html/web/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php).

Here is the content of buildForm:

public function buildForm(array $form, FormStateInterface $form_state) {
    /** @var SymfonyComponentHttpFoundationSessionSession $session */
    $session = $this->requestStack->getCurrentRequest()->getSession();
    /** @var DrupalnodeEntityNode $node */
    $node = $this->requestStack->getCurrentRequest()->get('node');
    if (!$session->has($node->id().'_register_selection')) {
      $this->requestStack->getCurrentRequest()->getSession()->set($node->id().'_register_selection', ());
    } else {
      $selected = $session->get($node->id().'_register_selection');
    }

    $form('selected_users') = (
      '#type' => 'container',
      '#attributes' => ('id' => 'selected-users'),
    );

    $users = $form_state->getValue('user_list');
    if (!empty($users)) {
      foreach ($users as $user) {
        if ($user('check') == TRUE) {
          $selected($user('uid')) = $user('uid');
        } elseif (array_key_exists($user('uid'), $users)) {
            unset($selected($user('uid')));
        }
        $session->set($node->id() . '_register_selection', $selected);
      }
    }

    if (!empty($selected)) {
      foreach ($selected as $item) {
        $form('selected_users')($item)('user') = (
          '#type' => 'item',
          '#markup' => t('user: @user', ('@user' => $item)),
          '#weight' => '0',
        );
      }
    }

    $header = (
      ('data' => $form('user_list')('check_all') = (
          '#type' => 'checkbox',
          '#title' => t('Select all'),
          '#return_value' => TRUE,
          '#wrapper_attributes' => (
            'class' =>('align-middle', 'm-0')
          )
        ),
        'class'=> ('py-4')
      ),
      'uid' => ('data' => t('UID'), 'class'=> ('align-middle', 'py-4')),
      'username' => ('data' => t('Username'), 'class'=> ('align-middle', 'py-4')),
      'sits_id' => ('data' => t('SITS ID'), 'class'=> ('align-middle', 'py-4')),
      'name' => ('data' => t('Name'), 'class'=> ('align-middle', 'py-4')),
      'mail' => ('data' => t('Email'), 'class'=> ('align-middle', 'py-4')),
      'college' => ('data' => t('College'), 'class'=> ('align-middle', 'py-4')),
      'programme' => ('data' => t('Programme'), 'class'=> ('align-middle', 'py-4')),
      'course' => ('data' => t('Course'), 'class'=> ('align-middle', 'py-4')),
      'year' => ('data' => t('Year'), 'class'=> ('align-middle', 'py-4')),
    );

    $form('user_list') = (
      '#type' => 'table',
      '#header' => $header,
      '#empty' => t('No users found with the selected filters.'),
      '#prefix' => "
", '#suffix' => "
", '#attributes' => ( 'class' => ('table-sm'), 'style' => 'font-size: .85rem', ) ); $query = $this->database->select('users_field_data', 'ufd'); $query->leftJoin('profile', 'profile', 'profile.uid = ufd.uid'); $query->leftJoin('profile__field_prof_sits_id', 'sitsid', 'sitsid.entity_id = profile.profile_id'); $query->leftJoin('profile__field_prof_first_name', 'fname', 'fname.entity_id = profile.profile_id'); $query->leftJoin('profile__field_prof_last_name', 'lname', 'lname.entity_id = profile.profile_id'); $query->leftJoin('profile__field_prof_college', 'college', 'college.entity_id = profile.profile_id'); $query->leftJoin('profile__field_prof_programme', 'programme', 'programme.entity_id = profile.profile_id'); $query->leftJoin('profile__field_prof_course', 'course', 'course.entity_id = profile.profile_id'); $query->leftJoin('profile__field_prof_sits_year', 'year', 'year.entity_id = profile.profile_id'); $query->fields('ufd', ('uid','name', 'mail')); $query->addField('sitsid', 'field_prof_sits_id_value', 'sits_id'); $query->addField('fname', 'field_prof_first_name_value', 'first_name'); $query->addField('lname', 'field_prof_last_name_value', 'last_name'); $query->addField('college', 'field_prof_college_target_id', 'college_gid'); $query->addField('programme', 'field_prof_programme_target_id', 'programme_gid'); $query->addField('course', 'field_prof_course_target_id', 'course_gid'); $query->addField('year', 'field_prof_sits_year_value', 'year'); $query->extend('DrupalCoreDatabaseQueryTableSortExtender')->orderByHeader($header); $pager = $query->extend('DrupalCoreDatabaseQueryPagerSelectExtender')->limit(10); $results = $pager->execute()->fetchAll(); if ($results) { foreach ($results as $data) { $form('user_list')($data->uid)('check') = ( '#type' => 'checkbox', '#ajax' => ( 'callback' => '::processUser', 'wrapper' => 'selected-users', ), '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0', 'p-0', 'text-align-center') ), '#attributes' => ( 'class' => ('m-0'), 'style' => 'position:relative;', ), '#default_value' => isset($selected($data->uid)) ? TRUE : FALSE, ); $form('user_list')($data->uid)('uid') = ( '#type' => 'item', '#markup' => $data->uid, '#value' => $data->uid, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('username') = ( '#type' => 'item', '#markup' => $data->name, '#value' => $data->name, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('sits_id') = ( '#type' => 'item', '#markup' => $data->sits_id, '#value' => $data->sits_id, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('name') = ( '#type' => 'item', '#markup' => $data->first_name . ' ' . $data->last_name, '#value' => $data->first_name . ' ' . $data->last_name, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('mail') = ( '#type' => 'item', '#markup' => $data->mail, '#value' => $data->mail, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('college') = ( '#type' => 'item', '#markup' => $data->college_gid, '#value' => $data->college_gid, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('programme') = ( '#type' => 'item', '#markup' => $data->programme_gid, '#value' => $data->programme_gid, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('course') = ( '#type' => 'item', '#markup' => $data->course_gid, '#value' => $data->course_gid, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); $form('user_list')($data->uid)('year') = ( '#type' => 'item', '#markup' => $data->year, '#value' => $data->year, '#weight' => '0', '#wrapper_attributes' => ( 'class' => ('align-middle', 'm-0') ) ); } } $form('actions') = ( '#type' => 'actions', '#attributes' => ( 'class' => ('mb-4', 'd-flex', 'justify-content-center') ) ); $form('actions')('submit') = ( '#type' => 'submit', '#value' => $this->t('Register'), '#name' => 'update', ); $form('pager') = ('#type' => 'pager'); return $form; } }

And the processUser callback:

  public function processUser(array &$form, FormStateInterface $form_state) {
    return $form('selected_users');
  }

This seems to work correctly without pagination. However, if you enable pagination, it will be interrupted. Has anyone experienced that?

Website Design – Enables users to select large items to export from a paginated list

On a website I design, users have access to a search facility that lists items with potentially much content (they are meant to be grammar exercises so that each item has several grammatical issues). The current view uses pagination (6 elements per page) and looks like this:

Enter image description here

Users should be able to select in a special mode the items to be exported to a file. While there might be hundreds of elements and multiple results pages, I'd like the design to be usable on no more than 40-50 elements, and suggest limiting the search query if the result set is larger.

So I have a problem: users can select items to export from a list that does not fit on a single page because the items are so large. While there are many questions (1, 2, 3) and solutions to selecting items from a long list, these items are typically small, simple text captions of one to two words in length, in this case list authors or a grid-based one Layout with checkboxes can be used; this is impractical.

The solution I was thinking about was to switch from pagination to infinite scrolling and then, if the user clicked the Export button, load all elements from the server if the user had not scrolled down, and then simply allow individual items to be selected via checkboxes (ie a box next to each item will be displayed, if the item is to be exported, the box should be checked).

  • Are there any significant disadvantages to this approach? One problem I can see is that over 10 elements are likely to scroll a lot, due to the height of each element. How big is the problem?
  • Is there any other solution I could consider?
  • I think my solution should work on mobile as well, with nothing more than checkboxes required. Are there any difficulties I miss?

SEO – How to fix or handle dynamic paginated URLs in a classified website

We have a classified website where we have different categories like male clothes, feminine clothes and so on

Below is the URL structure of the paginated pages:

https://in.mywebsite.com/female-clothes?page=1
https://in.mywebsite.com/female-clothes?page=2 and so on

We recorded Canonical as self-canonical tags in the following ways:




In the search console, we did something else, namely the URL parameters mentioned as follows:

Parameter: page
Effects: Paginated
Crawl: Let Googlebot decide

I just want to ask if we did everything right to handle pagination on our website.

Please let me know if something has been done wrong and how best to solve the problem of pagination.

Many Thanks

Pagination – Best approach to updating all entries in a list that is paginated?

I have a list that is paginated on a website. It also has a function to select the number of entries per page.
A mass update is required for all.

What would be the best approach for that? It is important to me that a user doubts whether we refer to the current page or to the entire list.
A confirmation window with details or a detail message would be sufficient?

This is how it looks like now:

Enter the image description here

SEO – Google does not find content in paginated lists

We have a website that lists machine parts by code number. These show in a paginated list:

/ parts? page = 1 has records 1 to 25 from 356736.

We need canonical, Next, and previous Left. The list can be filtered by brand or by (partial) code number / parts is the total list, / parts? brand = brand name is a sublist and /parts?code=0.008 is another list. The, canonical, Next, and previous Links take this into account, so Next and previous Change only the page number (they retain the rest of the URL) canonical URL only retains relevant URL parameters.

Every single part has its own page / parts? part =[CodeNumber], The URL may contain a number of other parameters that are relevant to filtering the list. These are used to provide backlinks to the list, but not canonical link is the simple, tidy URL. There is none Next or previous Links for individual subpages.

There are also / SitemapAn HTML code that contains a link to every page, every product and every part of the website. A huge long list. (It refers to the overall list of replacement parts, but only to the first page of it, not to each page of the pagination.)

There are also /sitemap.xmlThis is a Sitemap index that points to eight individual sitemaps, between which are all the URLs displayed in the HTML Sitemap.


Searching for article code results in inconsistent results.

For some elements, usually at the top of the list, Google leads us directly to the unique side of this section. For a few others, including early in the list, Google puts us on the right page of the paginated list, which is a reasonable compromise.

For part numbers in the middle of the list Google brings us / SitemapThat's not ideal, but better than nothing.

For part codes in the second half of the list Google does not find them at all.

Can I do anything to improve our results?


While our SEO links (Next, previous, and canonical) are correct on this list and have been around for some time canonical Links in other parts of the site have been a bit messy so far (usually each page was self-contained, resulting in a certain amount of duplicate content). I repaired it today. Could that have an effect?

Indexing – Should paginated sites be included in the sitemap?

My site contains ads for boats, boat engines, boat rentals, etc. (www.mynautics.com).

There are ads on several pages. I have finished the pagination (not on the link above):

On some pages (sorting etc.) I have also added a canonical relationship.

Now I want to add pages to the sitemap generator, and I'm not sure if I should add all the URLs, for example:

  • example.com/list/page/1
  • example.com/list/page/2
  • example.com/list/page/3
  • example.com/list/page/4

Or should I just add example.com/list?