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.

Aperçu

Toutes les méthodes ajax effectuent ce qui suit :

  1. Côté client, l’option data (si elle est donnée en tant qu’objet) est filtrée par le hook ajax_request_data.

  2. 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.

  3. La méthode renvoie un objet jqXHR, qui peut être utilisé comme promesse - Promise.

  4. 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).

  5. L’objet de réponse est filtré par l’événement ajax_response.

  6. L’objet de réponse est utilisé pour créer la réponse HTTP.

  7. Côté client, les données de réponse sont filtrées par le hook ajax_response_data.

  8. La promesse - promise - jqXHR est résolue et toutes les fonctions de rappel success 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 utilisateur générés par elgg_register_success_message() et elgg_register_error_message() 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, sinon GET. Vous pouvez changer cela via options.method.

  • Si une valeur options.data non vide est donnée, la méthode par défaut est toujours POST.

  • Pour la mise en cache côté client, définissez la méthode options.method sur GET et options.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éfinissez options.data.elgg_fetch_deps = 0.

  • Toutes les méthodes acceptent une chaîne de requête comme 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
elgg_register_success_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) {
    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)

  • 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.

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/elgg-plugin.php
return [
    'routes' => [
        'default:myplugin:time' => [
            'path' => '/myplugin_time',
            'resource' => 'myplugin/time',
        ],
    ],
];

// in myplugin/views/default/resources/myplugin/time.php
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) {
    alert(output.rfc2822);
    alert(output.day);
});

Notes pour les chemins :

  • Les 3 hooks (voyez les Actions ci-dessus) auront le type path:<url_path>. Dans ce cas, « path: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) {
    $('.myplugin-link').html(output);
});

Notes pour les vues :

  • Les 3 hooks (voyez les Actions ci-dessus) auront le type view:<view_name>. Dans ce cas, « view:myplugin/get_link ».

  • output sera une chaîne avec le contenu de la vue générée.

  • Les données de la requête sont injectées dans la vue à l’intérieur de $vars.

  • 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 avec 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) {
    $('.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 le contenu de 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 transmettre des attributs ou des paramètres de l’élément de formulaire rendus par la vue input/form (c’est-à-dire normalement passés comme deuxième paramètre à elgg_view_form()), utilisez l’événement côté 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 avec get_input(), mais peuvent ne pas avoir le type que vous attendez ou peuvent avoir des clefs inattendues.

Envoyer des formulaires

Pour envoyer un formulaire avec Ajax, il suffit de passer le paramètre ajax avec des variables de formulaire :

echo elgg_view_form('login', ['ajax' => true]);

Redirections

Utilisez ajax.forward() pour démarrer une animation d’attente - spinner - et rediriger l’utilisateur vers une nouvelle destination.

var Ajax = require('elgg/Ajax');
var ajax = new Ajax();
ajax.forward('/activity');

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.

Supposons 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 hooks = require('elgg/hooks');

var ajax = new Ajax();

hooks.register(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é fournies sous forme de chaîne (par ex. $form.serialize()), les hooks de requête ne sont pas déclenchés.

Note

Le formulaire sera transformé en objet nommé FormData, et le type de contenu de la requête sera déterminé en conséquence.

Réagir à une réponse Ajax

L’événement ajax_response côté serveur peut être utilisé pour ajouter ou filtrer les données de réponse (ou 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(\Elgg\Event $event) {

    /* @var $response AjaxResponse */
    $response = $event->getValue();

    // 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_event_handler(AjaxResponse::RESPONSE_EVENT, 'view:foo', 'myplugin_append_ajax');

Pour capturer les métadonnées renvoyées au client, nous utilisons le hook ajax_response_data côté client :

// in your boot module
var Ajax = require('elgg/Ajax');
var hooks = require('elgg/hooks');

hooks.register(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 :

  1. Succès HTTP (200) avec un statut 0. Aucun appel elgg_register_error_message() n’a été fait sur le serveur.

  2. Succès HTTP (200) avec le statut -1. elgg_register_error_message() a été appelé.

  3. 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 callback done ne sont pas appelées.

Le premier et le troisième cas sont les plus courants dans le système. Utilisez les callbacks done et fail pour différencier le comportement en cas de succès et d’erreur.

ajax.action('entity/delete?guid=123').done(function (value, statusText, jqXHR) {
    // remove element from the page
}).fail(function() {
    // handle error condition if needed
});

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.