I am trying to get multiple forms to work in a views display. All of the forms uses AJAX, but they appear to be interfering with each other.
One form is the Views Bulk Operations form that turns the views table into a 'viewsForm'. The second form is a 'quick edit' form available on each row in the view. The problem is when I submit any of the forms from the quick edit, it tries to also submit the 'viewsForm' form (which it shouldn't be doing), resulting in validation errors on that form. It's also not picking up my ajax callback for my custom form, as witnessed by the 'ajax callback is empty or not callable' in the dblog.
If I disable views bulk operations, this works as intended, but with multiple forms on the page, I can't figure out how to tell the 'submit' button I am using to only submit the form belonging to the submit button.
I have provided my formbuilder class for reference
<?php
namespace Drupal\request_system\Form;
use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\ReplaceCommand;
use Drupal\Core\Ajax\HightlightCommand;
/**
* Provides a Request System form.
*/
class QuickEditForm extends FormBase {
public $sub_id = 0;
public $entity_id = 0;
/**
* {@inheritdoc}
*/
public function getFormId() {
return 'request_system_quick_edit-' . $this->sub_id;
}
/**
* {@inheritdoc}
*/
public function buildForm(array $form, FormStateInterface $form_state) {
$entity = \Drupal::entityTypeManager()->getStorage('lms_request')->load($this->entity_id);
$options = [];
$options['_none'] = '- Select One -';
if ($entity->bundle() == 'book_request') {
$statuses = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->loadByProperties([
'vid' => 'book_request_status',
]);
}
// Only show 'available from vendor' and 'Pending'
foreach ($statuses as $status) {
if ($status->getName() == 'Pending' || $status->getName() == 'Available From Vendor') {
$options[$status->id()] = $status->getName();
}
if ($status->id() == $entity->field_request_status->getString()) {
$options[$status->id()] = $status->getName();
}
}
$form['quick_edit'] = [
'#type' => 'container',
'#id' => 'quick-edit-wrapper-'. $this->sub_id,
];
$form['quick_edit']['status'] = [
'#type' => 'fieldset',
'#title' => 'Status Updates',
'#name' => 'update-wrapper',
];
if (!$entity->field_aph_shipment_number->isEmpty() || !$entity->field_library_shipment_number->isEmpty()) {
$form['quick_edit']['status']['value'] = [
'#type' => 'item',
'#title' => 'Request Status: ',
'#markup' => \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($entity->field_request_status->getString())->getName(),
];
// Display message indicating item is part of a shipment
$form['quick_edit']['status']['shipment_number'] = [
'#markup' => 'This request is part of a shipment.',
];
}
else {
// Disable this field if the request status is not 'Pending' or 'Available from Vendor', or if the item belongs to a shipment
$form['quick_edit']['status']['value'] = [
'#type' => 'select',
'#title' => 'Status',
'#options' => $options,
'#default_value' => $entity->field_request_status->getString(),
];
if ($entity->field_request_status->getString() != \Drupal\request_system\Controller\RequestSystemController::getStatus('Pending') && $entity->field_request_status->getString() != \Drupal\request_system\Controller\RequestSystemController::getStatus('Available From Vendor')) {
$form['quick_edit']['status']['value']['#disabled'] = TRUE;
}
}
$form['quick_edit']['status']['message'] = [
'#type' => 'textarea',
'#title' => $this->t('Message'),
];
$form['quick_edit']['status']['notify_user'] = [
'#type' => 'checkbox',
'#title' => 'Notify Borrower',
];
// Allow editing of the APH catalog #
$form['quick_edit']['other'] = [
'#type' => 'fieldset',
'#title' => 'Other',
'#name' => 'other-wrapper',
];
$form['quick_edit']['other']['aph_catalog_number'] = [
'#type' => 'textfield',
'#title' => 'APH Catalog #',
'#default_value' => $entity->field_attached_copy_aph_number->getString(),
'#description' => $entity->field_attached_copy_main_record->isEmpty() ? '' : 'Unable to change APH catalog number when a main record is assigned.',
'#disabled' => $entity->field_attached_copy_main_record->isEmpty() ? FALSE : TRUE,
];
$form['quick_edit']['id'] = [
'#type' => 'hidden',
'#value' => $this->entity_id,
];
$form['quick_edit']['actions'] = [
'#type' => 'actions',
];
$form['quick_edit']['actions']['submit'] = [
'#type' => 'submit',
'#value' => $this->t('Save'),
'#ajax' => [
'callback' => '::quickEditAjax',
'wrapper' => 'quick-edit-wrapper-'. $this->sub_id,
],
'#validate' => '::validate',
'#limit_validation_errors' => [['id'],['status']],
'#submit' => ['::quickEditAjaxSubmit'],
];
// $form['quick_edit']['actions']['cancel'] = [
// '#type' => 'submit',
// '#value' => 'Cancel',
// ];
return $form;
}
/**
* {@inheritdoc}
*/
public function validateForm(array &$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
$entity = \Drupal::entityTypeManager()->getStorage('lms_request')->load($values['id']);
if ($values['status'] == \Drupal\request_system\Controller\RequestSystemController::getStatus("Shipped From Loan Library")) {
if (count($entity->field_imcid->referencedEntities()) == 0) {
$form_state->setErrorByName('status','Cannot mark this item shipped since no library item is attached.');
}
}
}
/**
* {@inheritdoc}
*/
public function submitForm(array &$form, FormStateInterface $form_state) {
}
public function quickEditAjax(&$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
if ($form_state->hasAnyErrors()) {
$form['status_messages'] = [
'#type' => 'status_messages',
'#weight' => -1000,
];
$form['#sorted'] = FALSE;
}
$response = new AjaxResponse();
$status = \Drupal::entityTypeManager()->getStorage('taxonomy_term')->load($values['status']);
$response->addCommand(new ReplaceCommand('.request-status-'. $values['id'],$status->getName()));
$response->addCommand(new ReplaceCommand('#quick-edit-wrapper-'. $this->sub_id,$form));
$response->addCommand(new HightlightCommand('#row-'. $values['id']));
return $response;
}
public function quickEditAjaxSubmit(&$form, FormStateInterface $form_state) {
$values = $form_state->getValues();
// First load the entity
$entity = \Drupal::entityTypeManager()->getStorage('lms_request')->load($values['id']);
$entity->set('field_request_status',$values['status']);
$entity->save();
$form_state->setRebuild();
}
}
And the way I am rendering the form is via
$form = \Drupal::classResolver()->getInstanceFromDefinition('Drupal\request_system\Form\QuickEditForm');
$form->sub_id = $entity->id();
$form->entity_id = $entity->id();
$build['form'] = \Drupal::formBuilder()->getForm($form);
It appears that when the view is rendered, it's combining all of the forms into a single , so when you click 'Save' on the sub-form, it's actually submitting the views bulk operations form. I am not sure how to resolve this or to get it to stop doing this.
I have been banging my head on this for two weeks trying to get this resolved, and can't figure out what the problem is.
Any help would be GREATLY appreciated.
EDIT: I have attached a screenshot of what we are trying to achieve. Checkbox on the left is views bulk operation, 'quick edit' form is on the far right via 'expandable table column'