Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
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 26, 2016
1 parent c589e8e commit 292dc39
Show file tree
Hide file tree
Showing 6 changed files with 64 additions and 14 deletions.
13 changes: 13 additions & 0 deletions docs/guides/ajax.rst
Expand Up @@ -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
------------------
Expand Down Expand Up @@ -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
=====================

Expand Down
46 changes: 39 additions & 7 deletions engine/classes/Elgg/Ajax/Service.php
@@ -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
Expand All @@ -31,6 +33,11 @@ class Service {
*/
private $input;

/**
* @var Config
*/
private $amd_config;

/**
* @var bool
*/
Expand All @@ -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);
}
}

/**
Expand Down Expand Up @@ -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");
}
}

Expand All @@ -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()) {
Expand Down Expand Up @@ -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;
}

}
2 changes: 1 addition & 1 deletion engine/classes/Elgg/Di/ServiceProvider.php
Expand Up @@ -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) {
Expand Down
2 changes: 0 additions & 2 deletions engine/lib/views.php
Expand Up @@ -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'));
Expand Down
2 changes: 2 additions & 0 deletions views/default/elgg.js.php
Expand Up @@ -98,3 +98,5 @@
}

elgg.trigger_hook('boot', 'system');

require(['elgg/init', 'elgg/ready']);
13 changes: 9 additions & 4 deletions views/default/elgg/Ajax.js
Expand Up @@ -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.
Expand Down Expand Up @@ -133,7 +133,7 @@ define(function (require) {

options = options || {};
options.url = path;

return fetch(options, 'path:' + path.replace(/\/$/, ''));
};

Expand Down Expand Up @@ -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;
});

Expand Down

0 comments on commit 292dc39

Please sign in to comment.