web-dev-qa-db-ja.com

管理されたファイルのアップロードが完了した後にJS関数を呼び出す

Drupal Form API(FAPI)managed_file要素を使用してファイルのアップロードが完了した後でJavaScript関数を呼び出すにはどうすればよいですか?

4
Jaypan

関数の実行後、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を実行できるようにします。

5
Jaypan

@Jaypanの回答と@FLYのコメントに基づいて、FieldWidgetを作成しました。これにより、hook_managed_file_processhook_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));
1
ptitb