Ajax¶
Le module AMD elgg/Ajax
(introduit dans Elgg 2.1) fournit un ensemble de méthodes pour communiquer avec le serveur d’une manière concise et uniforme, ce qui permet aux plugins de collaborer sur les données de requête, la réponse du serveur et les données renvoyées côté client.
Le code client et serveur écrit pour l’API d’origine ne doit pas avoir besoin de modification.
Contenus
Aperçu¶
Toutes les méthodes ajax effectuent ce qui suit :
Côté client, l’option
data
(si elle est donnée en tant qu’objet) est filtrée par le hookajax_request_data
.La requête est faite au serveur, que ce soit en affichant une vue ou un formulaire, en appelant une action, ou en chargeant un chemin d’accès.
La méthode renvoie un objet
jqXHR
, qui peut être utilisé comme promesse (Promise).Le contenu généré par le serveur est transformé en objet de réponse (
Elgg\Services\AjaxResponse
) contenant une chaîne (ou une valeur parsée en JSON).L’objet de réponse est filtré par le hook
ajax_response
.L’objet de réponse est utilisé pour créer la réponse HTTP.
Côté client, les données de réponse sont filtrées par le hook
ajax_response_data
.La promesse (promise)
jqXHR
est résolue et toutes les fonctions de rappelsuccess
sont appelées.
Plus de notes :
Tous les hooks ont un type en fonction de la méthode et du premier argument. Voyez ci-dessous.
Par défaut, le module
elgg/spinner
est automatiquement utilisé lors des requêtes.Les messages destinés à l’utilisateur générés par “
system_message()
etregister_error()
sont collectés et affichés sur le client.Elgg intègre un gestionnaire d’erreurs par défaut qui affiche un message générique en cas d’échec de la sortie.
Les exceptions PHP ou les codes d’erreur HTTP d’accès refusé, ce qui entraîne l’utilisation du gestionnaire d’erreurs côté client.
La méthode HTTP par défaut est
POST
pour les actions, sinonGET
. Vous pouvez changer cela viaoptions.method
.Si une valeur
options.data
non vide est donnée, la méthode par défaut est toujoursPOST
.Pour la mise en cache côté client, définissez la méthode
options.method
surGET
etoptions.data.elgg_response_ttl
sur l’âge maximal que vous souhaitez en secondes.Pour enregistrer des messages système pour le chargement de la page suivante, définissez
options.data.elgg_fetch_messages = 0
. Vous pourriez vouloir faire cela si vous avez l’intention de rediriger l’utilisateur en fonction de la réponse.Pour empêcher l’API côté client d’exiger des modules AMD requis côté serveur avec
elgg_require_js()
, définissezoptions.data.elgg_fetch_deps = 0
.Toutes les méthodes acceptent une chaîne de requête dans le premier argument. Cette chaîne est transmise à l’URL de récupération, mais n’apparaît pas dans les types de hooks.
Effectuer des actions¶
Considérez cette action :
// in myplugin/actions/do_math.php
elgg_ajax_gatekeeper();
$arg1 = (int)get_input('arg1');
$arg2 = (int)get_input('arg2');
// will be rendered client-side
system_message('We did it!');
echo json_encode([
'sum' => $arg1 + $arg2,
'product' => $arg1 * $arg2,
]);
Pour l’exécuter, utilisez ajax.action('<action_name>', options)
:
var Ajax = require('elgg/Ajax');
var ajax = new Ajax();
ajax.action('do_math', {
data: {
arg1: 1,
arg2: 2
},
}).done(function (output, statusText, jqXHR) {
if (jqXHR.AjaxData.status == -1) {
return;
}
alert(output.sum);
alert(output.product);
});
Notes pour les actions :
- Tous les hooks ont le type
action:<action_name>
. Donc, dans ce cas, trois hooks seront déclenchés : côté client
"ajax_request_data", "action:do_math"
pour filtrer les données de la requête (avant qu’elle soit envoyée)côté serveur
"ajax_response", "action:do_math"
pour filtrer la réponse (après que l’action a été exécutée)côté client
"ajax_response_data", "action:do_math"
pour filtrer les données de la réponse (avant que le code appelant ne les reçoive)
- Tous les hooks ont le type
Les jetons CSRF sont ajoutés aux données de la requête.
La méthode par défaut est
POST
.Une URL d’action absolue peut être donnée à la place du nom d’action.
L’utilisation de
forward()
dans une action envoie simplement la réponse. L’URL indiquée n’est pas renvoyée au client.
Note
Lorsque vous définissez data
, utilisez ajax.objectify($form)
au lieu de $form.serialize()
. Cela permet au hook de plugin ajax_request_data
d’être déclenché, et à d’autres plugins de modifier / réagir à la requête.
Récupérer des données¶
Considérez ce script PHP qui s’exécute à http://example.org/myplugin_time
.
// in myplugin/start.php
elgg_register_page_handler('myplugin_time', 'myplugin_get_time');
function myplugin_get_time() {
elgg_ajax_gatekeeper();
echo json_encode([
'rfc2822' => date(DATE_RFC2822),
'day' => date('l'),
]);
return true;
}
Pour récupérer sa sortie, utilisez ajax.path('<url_path>', options)
.
var Ajax = require('elgg/Ajax');
var ajax = new Ajax();
ajax.path('myplugin_time').done(function (output, statusText, jqXHR) {
if (jqXHR.AjaxData.status == -1) {
return;
}
alert(output.rfc2822);
alert(output.day);
});
Notes pour les chemins :
Les 3 hooks (voir Actions ci-dessus) auront le type
path:<url_path>
. Dans ce cas, « chemin:myplugin_time ».Si le gestionnaire de page renvoie une page Web normale,
output
sera une chaîne contenant le HTML de la page.Une URL absolue peut être donnée à la place du nom du chemin.
Récupérer des vues¶
Considérez cette vue :
// in myplugin/views/default/myplugin/get_link.php
if (empty($vars['entity']) || !$vars['entity'] instanceof ElggObject) {
return;
}
$object = $vars['entity'];
/* @var ElggObject $object */
echo elgg_view('output/url', [
'text' => $object->getDisplayName(),
'href' => $object->getUrl(),
'is_trusted' => true,
]);
Comme il s’agit d’un fichier PHP, nous devons d’abord l’enregistrer pour Ajax :
// in myplugin_init()
elgg_register_ajax_view('myplugin/get_link');
Pour récupérer la vue, utilisez ajax.view('<view_name>', options)
:
var Ajax = require('elgg/Ajax');
var ajax = new Ajax();
ajax.view('myplugin/get_link', {
data: {
guid: 123 // querystring
},
}).done(function (output, statusText, jqXHR) {
if (jqXHR.AjaxData.status == -1) {
return;
}
$('.myplugin-link').html(output);
});
Notes pour les vues :
Les 3 hooks (voir Actions ci-dessus) auront le type
view:<view_name>
. Dans ce cas, « view:myplugin/get_link ».output
sera une chaîne avec la vue générée.Les données de la requête sont injectées dans
$vars
dans la vue.Si les données de la requête contiennent
guid
, le système définit$vars['entity']
à l’entité correspondante ou àfalse
si elle ne peut pas être chargée.
Avertissement
Dans les vues et les formulaires ajax, notez que $vars
peut être peuplé par les saisies du client. Les données sont filtrées comme get_input()
, mais peuvent ne pas avoir le type que vous attendez ou peuvent avoir des clefs inattendues.
Récupérer des formulaires¶
Considérez que nous avons une vue de formulaire. Nous l’enregistrons pour Ajax :
// in myplugin_init()
elgg_register_ajax_view('forms/myplugin/add');
Pour récupérer ceci utilisez ajax.form('<action_name>', options)
.
var Ajax = require('elgg/Ajax');
var ajax = new Ajax();
ajax.form('myplugin/add').done(function (output, statusText, jqXHR) {
if (jqXHR.AjaxData.status == -1) {
return;
}
$('.myplugin-form-container').html(output);
});
Notes pour les formulaires :
Les 3 hooks (voir Actions ci-dessus) auront le type
form:<action_name>
. Dans ce cas, « form:myplugin/add ».output
sera une chaîne avec la vue générée.Les données de la requête sont injectées dans
$vars
dans la vue de votre formulaire.Si les données de la requête contiennent
guid
, le système définit$vars['entity']
à l’entité correspondante ou àfalse
si elle ne peut pas être chargée.
Note
Seules les données de la requête sont transmises à la vue du formulaire demandé (c’est-à-dire en tant que troisième paramètre accepté par elgg_view_form()
). Si vous devez passer des attributs ou des paramètres de l’élément de formulaire rendu par la vue input/form
(c’est-à-dire normalement passé comme deuxième paramètre à elgg_view_form()
), utilisez le hook serveur view_vars, input/form
.
Avertissement
Dans les vues et les formulaires ajax, notez que $vars
peut être peuplé par les saisies du client. Les données sont filtrées comme get_input()
, mais peuvent ne pas avoir le type que vous attendez ou peuvent avoir des clefs inattendues.
Réagir (piggybacking) lors d’une requête Ajax¶
Le hook ajax_request_data
côté client peut être utilisé pour ajouter ou filtrer les données envoyées par une demande elgg/Ajax
.
Disons que lorsque la vue foo
est récupérée, nous voulons également envoyer au serveur quelques données :
// in your boot module
var Ajax = require('elgg/Ajax');
var elgg = require('elgg');
var ajax = new Ajax();
elgg.register_hook_handler(Ajax.REQUEST_DATA_HOOK, 'view:foo', function (name, type, params, data) {
// send some data back
data.bar = 1;
return data;
});
Ces données peuvent être lues côté serveur via get_input('bar');
.
Note
Si les données ont été données sous forme de chaîne (par ex. $form.serialize()
), les hooks de requête ne sont pas déclenchés.
Réagir (piggybacking) à une réponse Ajax¶
Le hook ajax_response
côté serveur peut être utilisé pour ajouter ou filtrer des données de réponse (ou des métadonnées).
Disons que lorsque la vue foo
est récupérée, nous voulons également envoyer au client quelques données supplémentaires :
use Elgg\Services\AjaxResponse;
function myplugin_append_ajax($hook, $type, AjaxResponse $response, $params) {
// alter the value being returned
$response->getData()->value .= " hello";
// send some metadata back. Only client-side "ajax_response" hooks can see this!
$response->getData()->myplugin_alert = 'Listen to me!';
return $response;
}
// in myplugin_init()
elgg_register_plugin_hook_handler(AjaxResponse::RESPONSE_HOOK, 'view:foo', 'myplugin_append_ajax');
Pour capturer les métadonnées envoyées au client, nous utilisons le hook ajax_response
côté client :
// in your boot module
var Ajax = require('elgg/Ajax');
var elgg = require('elgg');
elgg.register_hook_handler(Ajax.RESPONSE_DATA_HOOK, 'view:foo', function (name, type, params, data) {
// the return value is data.value
// the rest is metadata
alert(data.myplugin_alert);
return data;
});
Note
Seule la valeur data.value
est renvoyée à la fonction success
, ou disponible via l’interface Deferred.
Note
Elgg utilise ces mêmes hooks pour diffuser des messages système via les réponses elgg/Ajax
.
Gérer les erreurs¶
Les réponses correspondent à l’une de ces trois catégories :
Succès HTTP (200) avec un statut
0
. Aucun appelregister_error()
n’a été fait sur le serveur.Succès HTTP (200) avec le statut
-1
.register_error()
a été appelé.Erreur HTTP (4xx/5xx). Par ex. appeler une action avec des jetons expirés, ou une exception sur le serveur. Dans ce cas les fonctions de callbacks
done
etsuccess
ne sont pas appelées.
Vous devriez ne vous inquiéter que du 2e cas. Ceci peut être fait en regardant à jqXHR.AjaxData.status
:
ajax.action('entity/delete?guid=123').done(function (value, statusText, jqXHR) {
if (jqXHR.AjaxData.status == -1) {
// a server error was already displayed
return;
}
// remove element from the page
});
Requérir des modules AMD¶
Chaque réponse d’un service Ajax contiendra une liste de modules AMD requis côté serveur avec elgg_require_js(). Lorsque les données de réponse sont déballées, ces modules seront chargés de façon asynchrone - les plugins ne doivent pas s’attendre à ce que ces modules soient chargés dans leurs gestionnaires $.done() et $.then() et doivent utiliser require() pour tous les modules dont ils dépendent. En outre, les modules AMD ne doivent pas s’attendre à ce que le DOM ait été modifié par une demande Ajax lorsqu’ils sont chargés - les événements DOM doivent être délégués et les manipulations sur les éléments DOM doivent être retardées jusqu’à ce que toutes les requêtes Ajax aient été résolues.
APIs elgg.ajax historiques¶
Elgg 1.8 a introduit elgg.action
, elgg.get
, elgg.getJSON
, et d’autres méthodes qui se comportent de manière moins uniforme côté client et côté serveur.
elgg.action d’origine¶
Différences :
vous devez récupérer manuellement
output
à partir du wrapper renvoyéle gestionnaire de
success
sera exécuté même si l’action est bloquéele gestionnaire
success
recevra un objet wrapper. Vous devez rechercherwrapper.output
pas de hook ajax
elgg.action('do_math', {
data: {
arg1: 1,
arg2: 2
},
success: function (wrapper) {
if (wrapper.output) {
alert(wrapper.output.sum);
alert(wrapper.output.product);
} else {
// the system prevented the action from running, but we really don't
// know why
elgg.ajax.handleAjaxError();
}
}
});
notes sur elgg.action¶
Il est préférable de renvoyer une chaîne non vide, car cela facilite la validation dans la fonction
success
. Si l’action n’a pas été autorisée à s’exécuter pour une raison quelconque,wrapper.output
sera une chaîne vide.Vous pouvez utiliser le module elgg/spinner.
Elgg n’utilise pas
wrapper.status
pour quoi que ce soit, mais un appel àregister_error()
définit sa valeur sur-1
.Si l’action renvoie une chaîne non-JSON,
wrapper.output
va emballer cette chaîne.
elgg.action
est basé surjQuery.ajax
et renvoie un objetjqXHR
(comme Promise), si vous vouliez l’utiliser.Une fois l’action PHP terminée, d’autres plugins peuvent modifier l’emballage via le hook plugin
'output', 'ajax'
, qui filtre l’emballage comme un tableau (pas une chaîne JSON).Un appel
forward()
force le traitement de l’action et renvoie la sortie immédiatement, avec la valeurwrapper.forward_url
définie sur l’emplacement normalisé donné.Pour vous assurer que les actions Ajax ne peuvent être exécutées que via XHR, utilisez
elgg_ajax_gatekeeper()
.
emballage de réponse JSON de elgg.action¶
{
current_url: {String} "http://example.org/action/example/math", // not very useful
forward_url: {String} "http://example.org/foo", ...if forward('foo') was called
output: {String|Object} from echo in action
status: {Number} 0 = success. -1 = an error was registered.
system_messages: {Object}
}
Avertissement
Il est probablement préférable de s’appuyer uniquement sur la clef output
, et de la valider au cas où l’action PHP ne pourrait pas s’exécuter pour une raison quelconque, par exemple parce que l’utilisateur a été déconnecté ou parce qu’une attaque CSRF n’a pas fourni de jetons.
Avertissement
Si forward()
est utilisé dans une réponse à une demande ajax héritée (par exemple elgg.ajax
), Elgg répondra toujours avec ce wrapper, même si ce n’est pas dans une action.
Récupération d’une vue héritée¶
Un plugin peut utiliser un script d’affichage pour gérer les demandes XHR GET
. Voici un exemple simple d’une vue qui renvoie un lien vers un objet donné par son GUID :
// in myplugin_init()
elgg_register_ajax_view('myplugin/get_link');
// in myplugin/views/default/myplugin/get_link.php
if (empty($vars['entity']) || !$vars['entity'] instanceof ElggObject) {
return;
}
$object = $vars['entity'];
/* @var ElggObject $object */
echo elgg_view('output/url', [
'text' => $object->getDisplayName(),
'href' => $object->getUrl(),
'is_trusted' => true,
]);
elgg.get('ajax/view/myplugin/get_link', {
data: {
guid: 123 // querystring
},
success: function (output) {
$('.myplugin-link').html(output);
}
});
Le système de vues Ajax fonctionne de manière significativement différente du système d’action.
Il n’y a pas de contrôle d’accès sur la base du statut de session.
Les requêtes non XHR sont automatiquement rejetées.
Les variables GET sont injectées dans
$vars
dans la vue.Si la requête contient
$_GET['guid']
, le système définit$vars['entity']
à l’entité correspondante oufalse
si celle-ci ne peut pas être chargée.Il n’y a pas aucun
emaballage
(wrapper) placé autour de la sortie de la vue.Les messages/erreurs système ne doivent pas être utilisés, car ils ne s’affichent pas tant que l’utilisateur n’a pas chargé une autre page.
Selon le suffixe de la vue (.js, .html, .css, etc.), l’entête Content-Type correspondant est ajouté.
Avertissement
- Dans les vues et les formulaires ajax, notez que
$vars
peut être peuplé par les saisies du client. Les données sont filtrées comme get_input()
, mais peut ne pas être le type que vous attendez ou peut avoir des clefs inattendues.
Renvoyer du JSON depuis une vue¶
Si les sorties des vues sont encodées en JSON, vous devez utiliser elgg.getJSON
pour les récupérer (ou utiliser une autre méthode pour définir l’option ajax dataType
de jQuery sur json
). Votre fonction success
recevra l’objet décodé.
Voici un exemple de récupération d’une vue qui renvoie un tableau d’heures encodé en JSON :
elgg.getJSON('ajax/view/myplugin/get_times', {
success: function (data) {
alert('The time is ' + data.friendly_time);
}
});
Récupération de formulaire d’origine¶
Si vous enregistrez une vue de formulaire (nom commençant par forms/
), vous pouvez la récupérer pré-générée avec elgg_view_form()
. Il suffit d’utiliser ajax/form/<action>
(au lieu de ajax/view/<view_name>
) :
// in myplugin_init()
elgg_register_ajax_view('forms/myplugin/add');
elgg.get('ajax/form/myplugin/add', {
success: function (output) {
$('.myplugin-form-container').html(output);
}
});
Seules les données de la requête sont transmises à la vue du formulaire demandé (c’est-à-dire en tant que troisième paramètre accepté par elgg_view_form()
). Si vous devez passer des attributs ou des paramètres de l’élément de formulaire rendu par la vue input/form
(c’est-à-dire normalement passé comme deuxième paramètre à elgg_view_form()
), utilisez le hook serveur view_vars, input/form
.
Avertissement
- Dans les vues et les formulaires ajax, notez que
$vars
peut être peuplé par les saisies du client. Les données sont filtrées comme get_input()
, mais peut ne pas être le type que vous attendez ou peut avoir des clefs inattendues.
Fonctions d’aide d’origine¶
Ces fonctions étendent les fonctionnalités Ajax natives de jQuery.
elgg.get()
est un wrapper pour $.ajax()
de jQuery, mais force GET
et normalise l’URL.
// normalizes the url to the current <site_url>/activity
elgg.get('/activity', {
success: function(resultText, success, xhr) {
console.log(resultText);
}
});
elgg.post()
est un wrapper pour $.ajax()
de jQuery, mais force POST
et normalise l’URL.