Drupal Form API(FAPI)managed_file
要素を使用してファイルのアップロードが完了した後でJavaScript関数を呼び出すにはどうすればよいですか?
関数の実行後、ajaxコードはフックやコールバックを提供しないため、これはD8ではトリッキーです。解決策は、managed_file要素が使用するajaxコールバック関数をオーバーライドし、ファイルのアップロードが完了したときにトリガーされる追加のカスタムコマンドを追加することです。
まず、カスタムAJAXコマンドを作成します。これは、実行するコードをトリガーするために使用されます。カスタムajaxコマンドの作成に関する詳細は、 here を参照してください。
最初に、PHP側のAjaxコマンド。これは_[MODULE]/src/Ajax
_に入ります。
_<?php
namespace Drupal\[MODULE]\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* Command to trigger an event when managed file upload is complete.
*/
class ManagedFileUploadCompleteEventCommand implements CommandInterface {
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return [
'command' => 'triggerManagedFileUploadComplete',
];
}
}
_
次に、triggerManagedFileUploadComplete()
コマンドをJavaScript側で作成する必要があります。これは_[MODULE]/js/managed_file_upload_complete_event_command.js
_で行われます。
_(function (Drupal) {
"use strict";
/**
* Add new custom command.
*/
Drupal.AjaxCommands.prototype.triggerManagedFileUploadComplete = function () {
// Do stuff here after file upload is complete.
alert(Drupal.t("File upload complete!"));
};
}(Drupal));
_
ここで、新しいJavaScriptファイルをMODULE.libraries.ymlに登録する必要があります。
_command.managed_file_upload_complete_event_command:
js:
js/managed_file_upload_complete_event_command.js: {}
dependencies:
- core/jquery
- core/jquery.once
_
これにより、ライブラリを使用してカスタムコマンドをアタッチできます:_[MODULE]/command.managed_file_upload_complete_event_command
_
カスタムコマンドとそれに関連するJSコールバックが完了したので、次は、新しいコマンドをajaxコールバックの最後に追加して、アップロードが完了すると実行されるようにします。
_managed_file
_要素のデフォルトのajaxコールバックは \ Drupal\file\Element\ManagedFile :: uploadAjaxCallback() です。これをオーバーライドして、上記で作成したコマンドを追加して、他のすべてのajaxコマンドの後に実行する必要があります。
_managed_file
_要素の#ajaxプロパティが \ Drupal\file\Element\ManagedFile :: processManagedFile() に追加されました。そのため、ajaxを変更する場合は、上記の#processハンドラーが実行された後に行う必要があります。そこで、デフォルトの#processコールバックの後に呼び出される別の#processコールバックを追加します。プロセスコールバックでは、#ajaxコールバックを独自の関数に変更できます。 #processコールバックは \ Drupal\file\Element\ManagedFile :: getInfo() で宣言されています。したがって、別の#processハンドラーの追加は hook_element_info_alter() で実行できます。
_/**
* Implements hook_element_info_alter().
*/
function MODULE_element_info_alter(array &$info) {
// Add a custom #process hook to the managed_file element:
$info['managed_file']['#process'][] = 'MODULE_managed_file_process';
// Add the custom command to managed_file elements, so that it is
// available when called:
$info['managed_file']['#attached']['library'][] = '[MODULE]/command.managed_file_upload_complete_event_command';
}
_
これでコマンドが作成され、すべての_managed_file
_要素にアタッチされました。次のステップは、#ajaxコールバックを独自のものでオーバーライドし、それにカスタムコマンドを追加することです。オーバーライドされた関数を見てみましょう(.moduleファイルに配置する必要があります):
_/**
* Custom ajax callback for managed files.
*
* Overrides \Drupal\file\Element\ManagedFile\ManagedFile::uploadAjaxCallback()
*
* @see \Drupal\file\Element\ManagedFile\ManagedFile::uploadAjaxCallback
*/
function MODULE_managed_file_ajax_callback(array &$form, FormStateInterface $form_state) {
// Retrieve the original response.
$response = \Drupal\file\Element\ManagedFile\ManagedFile::uploadAjaxCallback($form, $form_state, \Drupal::request());
// Add our own command to the end, so our command is run last:
$response->addCommand(new \Drupal\[MODULE]\Ajax\ManagedFileUploadCompleteEventCommand());
return $response;
}
_
最後に、元のajaxコマンドを独自のコマンドで置き換えます。これは、hook_widget_info_alter()に登録したプロセスコールバックで行われます。
_/**
* Custom process callback added to managed_file elements.
*
* Replaces the original #ajax callback with a custom one.
*/
function MODULE_managed_file_process(array &$element, FormStateInterface $form_state) {
$element['upload_button']['#ajax']['callback'] = 'MODULE_managed_file_ajax_callback';
return $element;
}
_
これは、元のajaxコールバックをMODULE_managed_file_ajax_callback()
で置き換えるようにDrupalに指示します。このコールバックは、元のコールバックから応答を取得し、カスタムコマンドをその最後に追加します。カスタムコマンドはファイルがアップロードされたときに呼び出され、ファイルのアップロードが完了した後、サーバー側で必要なJSを実行できるようにします。
@Jaypanの回答と@FLYのコメントに基づいて、FieldWidgetを作成しました。これにより、hook_managed_file_process
とhook_element_info_alter
の必要性が実際になくなりました。これにより、ウィジェットをフィールドごとに使用することも可能になりました。
まず、[MODULE]/src/Plugin/Field/FieldWidget
でカスタムFieldWidgetを作成します。
namespace Drupal\[MODULE]\Plugin\Field\FieldWidget;
use Drupal\Core\Form\FormStateInterface;
use Drupal\file\Plugin\Field\FieldWidget\FileWidget as CoreFileWidget;
/**
* @FieldWidget(
* id = "[MODULE]_file_widget",
* label = @Translation("[MODULE] file upload widget"),
* field_types = {
* "file"
* }
* )
*/
class FileWidget extends CoreFileWidget {
/**
* Override to replace the upload/file HTML control
* with the [MODULE] form element.
*
*/
public static function process($element, FormStateInterface $form_state, $form) {
$element = parent::process($element, $form_state, $form);
if (!isset($element['upload'])) {
return $element;
}
$element['upload_button']['#ajax']['callback'] = [get_called_class(), '[MODULE]ManagedFileAjaxCallback'];
$element['upload_button']['#attached']['library'][] = '[MODULE]/[MODULE].managed_file_upload_complete_event_command';
return $element;
}
/**
* Custom ajax callback for managed files.
*
* Overrides \Drupal\file\Element\ManagedFile\ManagedFile::uploadAjaxCallback()
*
* @see \Drupal\file\Element\ManagedFile\ManagedFile::uploadAjaxCallback
*/
public function [MODULE]ManagedFileAjaxCallback(array &$form, FormStateInterface $form_state) {
// Retrieve the original response.
$response = \Drupal\file\Element\ManagedFile::uploadAjaxCallback($form, $form_state, \Drupal::request());
// Add our own command to the end, so our command is run last:
$response->addCommand(new \Drupal\[MODULE]\Ajax\ManagedFileUploadCompleteEventCommand($form, $form_state));
return $response;
}
}
次に、Jaypanがすでに[MODULE]/src/Ajax
にカスタムAjaxコマンド用のファイルを作成しているようにします(ファイルにphp拡張子を付けたクラスの名前を付けます)。
namespace Drupal\[MODULE]\Ajax;
use Drupal\Core\Ajax\CommandInterface;
/**
* Command to trigger an event when managed file upload is complete.
*/
class ManagedFileUploadCompleteEventCommand implements CommandInterface {
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
return [
'command' => 'triggerManagedFileUploadComplete',
];
}
}
[MODULE]/js/managed_file_upload_complete_event_command.js
のコマンドによってトリガーされるJavaScriptファイルを作成します。
(function (Drupal) {
"use strict";
/**
* Add new custom command.
*/
Drupal.AjaxCommands.prototype.triggerManagedFileUploadComplete = function () {
// Do stuff here after file upload is complete.
alert(Drupal.t("File upload complete!"));
};
}(Drupal));
JavaScriptファイルを[MODULE].libraries.yml
に登録します。
[MODULE].managed_file_upload_complete_event_command:
js:
js/managed_file_upload_complete_event_command.js: {}
dependencies:
- core/jquery
- core/jquery.once
必要に応じて、カスタムコマンドで$form
および$form_state
を使用して、JavaScriptでこれを使用することもできます。
アップロードされているファイルとは異なる値を使用してしまいました。私の[MODULE]/src/Ajax/ManagedFileUploadCompleteEventCommand.php
は次のようになります:
namespace Drupal\[MODULE]\Ajax;
use Drupal\Core\Ajax\CommandInterface;
use Drupal\file\Entity\File;
/**
* Command to trigger an event when managed file upload is complete.
*/
class ManagedFileUploadCompleteEventCommand implements CommandInterface {
// Constructs a ReadMessageCommand object.
public function __construct($form) {
$this->form = $form;
}
/**
* Implements Drupal\Core\Ajax\CommandInterface:render().
*/
public function render() {
$files = array();
$form_delta = $this->form['#file_upload_delta'];
for($i=0;$i<$form_delta;$i++) {
$file_id = array_keys($this->form[$i]['#files'])[0];
$file = File::load($file_id);
$uri = $file->getFileUri();
$stream_wrapper_manager = \Drupal::service('stream_wrapper_manager')->getViaUri($uri);
$file_path = $stream_wrapper_manager->getExternalUrl();
$file_name = $file->getFilename();
$files[$i] = array(
'file_id' => $file_id,
'file_path' => $file_path,
'file_name' => $file_name,
);
}
return [
'command' => 'triggerManagedFileUploadComplete',
'files' => $files,
];
}
}
次のように、[MODULE]/js/managed_file_upload_complete_event_command.js
で作成したJavaScriptファイルでそれらを使用できます。
(function (Drupal) {
"use strict";
/**
* Add new custom command.
*/
Drupal.AjaxCommands.prototype.triggerManagedFileUploadComplete = function (ajax, response, status) {
var files = response.files;
// Do stuff here after file upload is complete.
console.log(files.file_id);
console.log(files.file_path);
console.log(files.file_name);
};
}(Drupal));