I'm loading the node/edit form in a block and I add ajax on submit.
I add the Ajax in the hook_form_alter.
$form['#prefix'] = '<div id="' . $form_wrapper . '">';
$form['#suffix'] = '</div>';
$form['actions']['submit']['#submit'][] = 'submitForm';
$form['actions']['submit']['#ajax'] = array(
'callback' => 'my_ajax_callback',
'wrapper' => $form_wrapper,
'effect' => 'fade',
);
The SubmitForm:
function submitForm(array &$form, FormStateInterface $form_state){
$input = $form_state->getUserInput();
$form_state->setUserInput($input);
$form_state->setRebuild(TRUE);
}
And the callback :
function my_ajax_callback(&$form, FormStateInterface $form_state, $form_id) {
$form_state->setRebuild();
return $form;
}
The first time works fine. The second time seems to work without any error but it doesn't submit my new data.
I checked this comment How can I implement AJAX form submission? but I don't know if I have to set the values to the form_state. Also i use this callback for multiple content types. Should I check and set the value for each field? Is there a better way?
Thanks for your help!
------ EDIT -----
I added the submitForm function and now the second time I submit I get 'The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'.
Also, should I get/set each one of my fields or $input = $form_state->getUserInput(); $form_state->setUserInput($input); are enough?
-------- UPDATE ------
I managed to make it work for forms that I need to be empty after the ajax submit using the below code :
In hook_form_alter:
$form['wrapper'] = array(
'#type' => 'container',
'#attributes' => array('id' => $form_wrapper),
);
$prefix = '<div id="' . $form_wrapper . '">';
$form['#prefix'] = $prefix;
$form['#suffix'] = '</div>';
$form['actions']['submit']['#submit'][] = 'Drupal\MY_MODULE\Plugin\Form\FormAlter::ajaxFormEntityFormSubmitEmptyForm';
$form['actions']['submit']['#ajax'] = array(
'callback' => 'Drupal\MY_MODULE\Plugin\Form\FormAlter::ajaxFormEntityCallbackEmptyForm',
'wrapper' => $form_wrapper,
'effect' => 'fade',
);
And in the FormAlter.php :
/**
* Rebuild the form - Empty form.
*/
public static function ajaxFormEntityFormSubmitEmptyForm($form, &$form_state){
$entity = $form_state->getBuildInfo()['callback_object']->getEntity();
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$new_entity = \Drupal::entityTypeManager()->getStorage($entity_type)->create(['type' => $bundle]);
$form_state->getBuildInfo()['callback_object']->setEntity($new_entity);
// Clear user input.
$input = $form_state->getUserInput();
$clean_keys = $form_state->getCleanValueKeys();
$clean_keys[] = 'ajax_page_state';
foreach ($input as $key => $item) {
if (!in_array($key, $clean_keys) && substr($key, 0, 1) !== '_') {
unset($input[$key]);
}
}
$input['entity'] = $entity;
$form_state->setUserInput($input);
// Rebuild the form state values.
$form_state->setRebuild();
$form_state->setStorage([]);
}
/**
* Ajax callback to handle just returns the form
*/
public static function ajaxFormEntityCallbackEmptyForm(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
if ($form_state->hasAnyErrors()) {
return $form;
}
else {
return $form;
}
}
Now I'm trying to do something similar but I want the form populated with the new values. So I created two new functions in FormAlter:
/**
* Rebuild the form.
*/
public static function ajaxFormEntityFormSubmit($form, &$form_state) {
$entity = $form_state->getBuildInfo()['callback_object']->getEntity();
$entity_type = $entity->getEntityTypeId();
$bundle = $entity->bundle();
$new_entity = \Drupal::entityTypeManager()->getStorage($entity_type)->create(['type' => $bundle]);
$form_state->getBuildInfo()['callback_object']->setEntity($new_entity);
$input = $form_state->getUserInput();
$input['entity'] = $entity;
$form_state->setUserInput($input);
if(!$form_state->hasValue('title')){
$form_state->setValue('title',$entity->getTitle());
}
// Rebuild the form state values.
$form_state->setRebuild(TRUE);
$form_state->setStorage([]);
}
/**
* Ajax callback to handle special ajax form entity magic.
*/
public static function ajaxFormEntityCallback(&$form, \Drupal\Core\Form\FormStateInterface $form_state) {
// If errors, returns the form with errors and messages.
if ($form_state->hasAnyErrors()) {
return $form;
}
// Else show the result.
else {
$userInputs = $form_state->getUserInput();
$entity = $userInputs['entity'];
$entity_type = $entity->getEntityTypeId();
$response = new AjaxResponse();
if ($entity_type == 'node') {
$title = $entity->getTitle();
}
return $form;
}
}
If I return the $form in the callback, the first time works fine but the second I'm getting two errors in the log:
Drupal\Core\Database\IntegrityConstraintViolationException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null: INSERT INTO {node_field_data} (nid, vid, type, langcode, status, title, uid, created, changed, promote, sticky, revision_translation_affected, default_langcode) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12); Array ( [:db_insert_placeholder_0] => 201 [:db_insert_placeholder_1] => 258 [:db_insert_placeholder_2] => my_estate_details [:db_insert_placeholder_3] => en [:db_insert_placeholder_4] => 1 [:db_insert_placeholder_5] => [:db_insert_placeholder_6] => 5 [:db_insert_placeholder_7] => 1500889059 [:db_insert_placeholder_8] => 1500889066 [:db_insert_placeholder_9] => 0 [:db_insert_placeholder_10] => 0 [:db_insert_placeholder_11] => 1 [:db_insert_placeholder_12] => 1 ) in Drupal\Core\Database\Connection->handleQueryException()
And
Drupal\Core\Entity\EntityStorageException: SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'title' cannot be null: INSERT INTO {node_field_data} (nid, vid, type, langcode, status, title, uid, created, changed, promote, sticky, revision_translation_affected, default_langcode) VALUES (:db_insert_placeholder_0, :db_insert_placeholder_1, :db_insert_placeholder_2, :db_insert_placeholder_3, :db_insert_placeholder_4, :db_insert_placeholder_5, :db_insert_placeholder_6, :db_insert_placeholder_7, :db_insert_placeholder_8, :db_insert_placeholder_9, :db_insert_placeholder_10, :db_insert_placeholder_11, :db_insert_placeholder_12); Array ( [:db_insert_placeholder_0] => 201 [:db_insert_placeholder_1] => 258 [:db_insert_placeholder_2] => my_estate_details [:db_insert_placeholder_3] => en [:db_insert_placeholder_4] => 1 [:db_insert_placeholder_5] => [:db_insert_placeholder_6] => 5 [:db_insert_placeholder_7] => 1500889059 [:db_insert_placeholder_8] => 1500889066 [:db_insert_placeholder_9] => 0 [:db_insert_placeholder_10] => 0 [:db_insert_placeholder_11] => 1 [:db_insert_placeholder_12] => 1 ) in Drupal\Core\Entity\Sql\SqlContentEntityStorage->save()
In case I return $response (which is what I prefer in order to be able to alter the message etc), the first time works fine but the second I get the message:
The content has either been modified by another user, or you have already submitted modifications. As a result, your changes cannot be saved.'.
Apologies for the long message. Any idea/help will be helpful!
Thanks in advance.