Permalink
Browse files

feature(ajax): Ajax service now loads required AMD modules

AMD modules required server-side with elgg_require_js() are now recognized
and loaded by the Ajax service upon a successfull request client-side.
  • Loading branch information...
hypeJunction committed Mar 24, 2016
1 parent c589e8e commit 292dc391355b9fc6c5116f54b2d8ce20770469af
View
@@ -32,6 +32,7 @@ More notes:
* The default HTTP method is ``POST`` for actions, otherwise ``GET``. You can set it via ``options.method``.
* For client caching, set ``options.method`` to ``"GET"`` and ``options.data.elgg_response_ttl`` to the max-age you want in seconds.
* To save system messages for the next page load, set ``options.data.elgg_fetch_messages = 0``. You may want to do this if you intent to redirect the user based on the response.
+* To stop client-side API from requiring AMD modules required server-side with `elgg_require_js()`, set ``options.data.elgg_fetch_deps = 0``.
Performing actions
------------------
@@ -284,6 +285,18 @@ To capture the metadata send back to the client, we use the client-side ``ajax_r
.. note:: Elgg uses these same hooks to deliver system messages over ``elgg/Ajax`` responses.
+
+Requiring AMD modules
+---------------------
+
+Each response from an Ajax service will contain a list of AMD modules required server side with `elgg_require_js()`.
+When response data is unwrapped, these modules will be loaded asynchronously - plugins should not expect these
+modules to be loaded in their `$.done()` and `$.then()` handlers and must use `require()` for any modules they depend on.
+Additionally AMD modules should not expect the DOM to have been altered by an Ajax request when they are loaded -
+DOM events should be delegated and manipulations on DOM elements should be delayed until all Ajax requests have been
+resolved.
+
+
Legacy elgg.ajax APIs
=====================
@@ -1,11 +1,13 @@
<?php
namespace Elgg\Ajax;
+use Elgg\Amd\Config;
use Elgg\Http\Input;
use Elgg\PluginHooksService;
+use Elgg\Services\AjaxResponse;
use Elgg\SystemMessagesService;
+use RuntimeException;
use Symfony\Component\HttpFoundation\JsonResponse;
-use Elgg\Services\AjaxResponse;
/**
* Models the Ajax API service
@@ -31,6 +33,11 @@ class Service {
*/
private $input;
+ /**
+ * @var Config
+ */
+ private $amd_config;
+
/**
* @var bool
*/
@@ -39,19 +46,26 @@ class Service {
/**
* Constructor
*
- * @param PluginHooksService $hooks Hooks service
- * @param SystemMessagesService $msgs System messages service
- * @param Input $input Input service
+ * @param PluginHooksService $hooks Hooks service
+ * @param SystemMessagesService $msgs System messages service
+ * @param Input $input Input service
+ * @param Config $amdConfig AMD config
*/
- public function __construct(PluginHooksService $hooks, SystemMessagesService $msgs, Input $input) {
+ public function __construct(PluginHooksService $hooks, SystemMessagesService $msgs, Input $input, Config $amdConfig) {
$this->hooks = $hooks;
$this->msgs = $msgs;
$this->input = $input;
+ $this->amd_config = $amdConfig;
if ($this->input->get('elgg_fetch_messages', true)) {
$message_filter = [$this, 'appendMessages'];
$this->hooks->registerHandler(AjaxResponse::RESPONSE_HOOK, 'all', $message_filter, 999);
}
+
+ if ($this->input->get('elgg_fetch_deps', true)) {
+ $deps_filter = [$this, 'appendDeps'];
+ $this->hooks->registerHandler(AjaxResponse::RESPONSE_HOOK, 'all', $deps_filter, 999);
+ }
}
/**
@@ -159,7 +173,7 @@ private function filterApiResponse(AjaxResponse $api_response, $hook_type = '')
$hook = AjaxResponse::RESPONSE_HOOK;
$api_response = $this->hooks->trigger($hook, $hook_type, null, $api_response);
if (!$api_response instanceof AjaxResponse) {
- throw new \RuntimeException("The value returned by hook [$hook, $hook_type] was not an ApiResponse");
+ throw new RuntimeException("The value returned by hook [$hook, $hook_type] was not an ApiResponse");
}
}
@@ -173,7 +187,7 @@ private function filterApiResponse(AjaxResponse $api_response, $hook_type = '')
* @param bool $allow_removing_headers Alter PHP's global headers to allow caching
*
* @return JsonResponse
- * @throws \RuntimeException
+ * @throws RuntimeException
*/
private function buildHttpResponse(AjaxResponse $api_response, $allow_removing_headers = true) {
if ($api_response->isCancelled()) {
@@ -219,4 +233,22 @@ public function appendMessages($hook, $type, AjaxResponse $response, $params) {
$response->getData()->_elgg_msgs = (object)$this->msgs->dumpRegister();
return $response;
}
+
+ /**
+ * Send required AMD modules list back with the response
+ *
+ * @param string $hook "ajax_response"
+ * @param string $type "all"
+ * @param AjaxResponse $response Ajax response
+ * @param array $params Hook params
+ *
+ * @return AjaxResponse
+ * @access private
+ * @internal
+ */
+ public function appendDeps($hook, $type, AjaxResponse $response, $params) {
+ $response->getData()->_deps = (array) $this->amd_config->getDependencies();
+ return $response;
+ }
+
}
@@ -109,7 +109,7 @@ public function __construct(\Elgg\Config $config) {
$this->setClassName('adminNotices', \Elgg\Database\AdminNotices::class);
$this->setFactory('ajax', function(ServiceProvider $c) {
- return new \Elgg\Ajax\Service($c->hooks, $c->systemMessages, $c->input);
+ return new \Elgg\Ajax\Service($c->hooks, $c->systemMessages, $c->input, $c->amdConfig);
});
$this->setFactory('amdConfig', function(ServiceProvider $c) {
View
@@ -1692,8 +1692,6 @@ function elgg_views_boot() {
elgg_load_css('elgg');
elgg_register_simplecache_view('elgg/init.js');
- elgg_require_js('elgg/init');
- elgg_require_js('elgg/ready');
// optional stuff
elgg_register_js('lightbox', elgg_get_simplecache_url('lightbox.js'));
@@ -98,3 +98,5 @@
}
elgg.trigger_hook('boot', 'system');
+
+require(['elgg/init', 'elgg/ready']);
View
@@ -38,9 +38,9 @@ define(function (require) {
*/
function fetch(options, hook_type) {
var orig_options,
- params,
- unwrapped = false,
- result;
+ params,
+ unwrapped = false,
+ result;
function unwrap_data(data) {
// between the deferred and a success function, make sure this runs only once.
@@ -133,7 +133,7 @@ define(function (require) {
options = options || {};
options.url = path;
-
+
return fetch(options, 'path:' + path.replace(/\/$/, ''));
};
@@ -214,6 +214,11 @@ define(function (require) {
m && m.error && elgg.register_error(m.error);
m && m.success && elgg.system_message(m.success);
delete data._elgg_msgs;
+
+ var deps = data._deps;
+ deps.length && require(deps);
+ delete data._deps;
+
return data;
});

0 comments on commit 292dc39

Please sign in to comment.