Using Drupal 8.7.7 I have a form which consists of a paginated table of users.
Each user row has a checkbox element in the form with an ajax event attached. This is essentially to allow me to store the selection in the users session as they select users and move to different pages.
The form works correctly on the first page, however when you move to the second page, only the first selection works. Any selection made after that throws the error:
Symfony\Component\HttpKernel\Exception\HttpException: The specified #ajax callback is empty or not callable. in Drupal\Core\Form\FormAjaxResponseBuilder->buildResponse() (line 67 of /var/www/html/web/core/lib/Drupal/Core/Form/FormAjaxResponseBuilder.php).
Here is the buildForm contents:
public function buildForm(array $form, FormStateInterface $form_state) {
/** @var \Symfony\Component\HttpFoundation\Session\Session $session */
$session = $this->requestStack->getCurrentRequest()->getSession();
/** @var \Drupal\node\Entity\Node $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' => "<div class='table-responsive mt-3'>",
'#suffix' => "</div>",
'#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('Drupal\Core\Database\Query\TableSortExtender')->orderByHeader($header);
$pager = $query->extend('Drupal\Core\Database\Query\PagerSelectExtender')->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. Enabling pagination makes it break. Has anyone experienced this before?