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 la requête, la réponse du serveur et les données renvoyées au 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. Server-echoed content is turned into an object

  5. The object is filtered by the event ajax_results.

  6. The object is used to create the HTTP response.

  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 requis côté serveur avec elgg_import_esm(), 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)

    • server-side "ajax_results", "action:do_math" to filter the response (after the action runs)

    • 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

The server-side ajax_results event can be used to append or filter response data (or metadata).

Disons que lorsque la vue foo est récupérée, nous voulons également envoyer au client quelques données supplémentaires :

function myplugin_append_ajax(\Elgg\Event $event) {

    /* @var $data /stdClass */
    $data = $event->getValue();

    // alter the value being returned
    $data->value .= " hello";

    // send some metadata back
    $data->myplugin_alert = 'Listen to me!';

    return $data;
}

// in myplugin_init()
elgg_register_event_handler('ajax_results', '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 ES

Chaque réponse d’un service Ajax contiendra une liste de modules ES requis côté serveur avec elgg_import_esm(). 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 import pour tous les modules dont ils dépendent. En outre, les modules ES 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.