after reading a tone of documentations and examples of other Drupal developers, I figured out a way which worked for me very good. But this may not a best practice way, after all ;-)
Here is what I've done, to add a new button to a node content type edit form:
how to build an async node save button
add new button to a content type
In your module add hook form_alter to you my_module.module file to add the new button to your content type edit form:
/**
* Implements hook_form_alter().
*/
function emp_save_edit_form_alter(&$form, FormStateInterface $form_state): void {
$formObject = $form_state->getFormObject();
$entity = $formObject->getEntity();
$contentType = $entity->getType();
if ($contentType === "YOUR_CONTENT_TYPE") {
$form['actions'][] = [
'#type' => 'submit',
'#value' => 'Async save',
// this is required to have the entity stored properly
'#submit' => [
'::submitForm',
'::save',
],
// this enables the button to used the async way
'#ajax' => [
'callback' => 'formSubmitAjax',
/*if you want to to use a progress bar instead of the spinning clock, the use this:
'progress' => [
'type'=> 'bar',
'message' => 'saving...'
]*/
],
'#weight' => 1,
];
}
}
This will a submit button named "Async save" to your edit form which should do the following:
- call the function submitForm and save of the node entity form (or any other entity form, if you like)
- call the function formSubmitAjax
Both are executed with ajax. The edit form should not be reloaded.
add function formSubmitAjax
Then also add the still missing function formSubmitAjax to your my_module.module file:
use Drupal\Core\Ajax\AjaxResponse;
use Drupal\Core\Ajax\InvokeCommand;
use Drupal\Core\Ajax\OpenModalDialogCommand;
use Drupal\Core\Ajax\ReplaceCommand;
/**
* Submit form with ajax - add messages to be shown after ajax request is processed
*/
function formSubmitAjax(&$form, FormStateInterface $form_state): AjaxResponse
{
$response = new AjaxResponse();
// ToDo add redirect when form is an "add-form"
// if functions "submitForm" or "save" will have errors, these should be collected in $form_state->errors
if ($errors = $form_state->getErrors()) {
foreach ($errors as $error) {
$errorHtml = strtr('<div class="messages messages--error">$errorText</div>', ['$errorText' => $error->render()]);
// show message as a drupal Dialog Modal
$response->addCommand(new OpenModalDialogCommand('Error', $errorHtml));
}
} else {
$date = new DateTime();
// add a message to show things worked out - with current timestamp
$successMsg = 'Your changes have been saved at ' . $date->format('Y-m-d H:i:s');
$successHtml = strtr('<div class="messages messages--status">$statusText</div>', ['$statusText' => $successMsg]);
$response->addCommand(new OpenModalDialogCommand('Saved', $successHtml));
}
$response->addCommand(new ReplaceCommand(NULL, $form));
// the changed time of the form has to be changed after a form is submitted to prevent the error "this page as already saved by another user" or so
$response->addCommand(new InvokeCommand('input[name="changed"]','val',[time()]));
// prevent messages to be shown after reloading the edit form, all recent messages have to be deleted
Drupal::messenger()->deleteAll();
return $response;
}
Te be sure this code find it's way into Drupal, rebuild your cache:
drush cr