jQuery passe plus de paramètres dans le rappel

255

Existe-t-il un moyen de passer plus de données dans une fonction de rappel dans jQuery?

J'ai deux fonctions et je veux que le rappel à $.post, par exemple, passe à la fois les données résultantes de l'appel AJAX, ainsi que quelques arguments personnalisés

function clicked() {
    var myDiv = $("#my-div");
    // ERROR: Says data not defined
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
    // ERROR: Would pass in myDiv as curData (wrong)
    $.post("someurl.php",someData,doSomething(data, myDiv),"json"); 
}

function doSomething(curData, curDiv) {

}

Je veux pouvoir transmettre mes propres paramètres à un rappel, ainsi que le résultat renvoyé par l'appel AJAX.

atp
la source
14
Il convient de mentionner que jQuery.ajax () a un paramètre de contexte depuis la version 1.4 ( jquery14.com/day-01/jquery-14 ) voir un exemple de son utilisation ici: stackoverflow.com/questions/5097191/ajax-context- option /…
russau
J'ai apprécié mon problème de retour des données une fois AJAX terminé, puis je fais quelque chose.
Onaiggac

Réponses:

340

La solution est la liaison des variables par la fermeture.


Comme exemple plus basique, voici un exemple de fonction qui reçoit et appelle une fonction de rappel, ainsi qu'un exemple de fonction de rappel:

function callbackReceiver(callback) {
    callback("Hello World");
}

function callback(value1, value2) {
    console.log(value1, value2);
}

Cela appelle le rappel et fournit un seul argument. Maintenant, vous voulez fournir un argument supplémentaire, vous devez donc boucler le rappel en fermeture.

callbackReceiver(callback);     // "Hello World", undefined
callbackReceiver(function(value) {
    callback(value, "Foo Bar"); // "Hello World", "Foo Bar"
});

Ou, plus simplement en utilisant les fonctions fléchées ES6 :

callbackReceiver(value => callback(value, "Foo Bar")); // "Hello World", "Foo Bar"

Quant à votre exemple spécifique, je n'ai pas utilisé la .postfonction dans jQuery, mais une analyse rapide de la documentation suggère que le rappel devrait être un pointeur de fonction avec la signature suivante:

function callBack(data, textStatus, jqXHR) {};

Je pense donc que la solution est la suivante:

var doSomething = function(extraStuff) {
    return function(data, textStatus, jqXHR) {
        // do something with extraStuff
    };
};

var clicked = function() {
    var extraStuff = {
        myParam1: 'foo',
        myParam2: 'bar'
    }; // an object / whatever extra params you wish to pass.

    $.post("someurl.php", someData, doSomething(extraStuff), "json");
};

Qu'est-ce qui se passe?

Dans la dernière ligne, doSomething(extraStuff)est invoqué et le résultat de cette invocation est un pointeur de fonction .

Parce qu'il extraStuffest passé en argument, doSomethingil est dans le cadre de la doSomethingfonction.

Lorsque extraStuffest référencé dans la fonction interne anonyme retournée, doSomethingil est lié par la fermeture à l' extraStuffargument de la fonction externe . Cela est vrai même après doSomethingson retour.

Je n'ai pas testé ce qui précède, mais j'ai écrit un code très similaire au cours des dernières 24 heures et cela fonctionne comme je l'ai décrit.

Vous pouvez bien sûr transmettre plusieurs variables au lieu d'un seul objet «extraStuff» en fonction de vos préférences personnelles / normes de codage.

Bradhouse
la source
Quel est le but de 'var doSomething ='? En quoi est-ce différent de simplement déclarer doSomething en tant que fonction (c'est-à-dire la fonction doSomething (...) {})
Dave Neeley
7
Ce n'est pas vrai. Des règles de portée différentes s'appliquent à ces deux déclarations de fonction. Le comportement diffère beaucoup selon le runtime JS (lire le navigateur). Essayez également de comparer le nom de la fonction affiché dans stacktrace / breakpoint dans Firebug.
Ihor Kaharlichenko
1
Ihor: Vous avez absolument raison sur la différence entre les deux styles de déclaration. Une bonne explication est ici: stackoverflow.com/questions/336859/…
bradhouse
2
Cela peut être un peu difficile à suivre pour les utilisateurs qui essaient d'appliquer cela à des problèmes plus généraux en raison de la spécificité de l'exemple. J'ai ajouté un exemple simplifié pour le rendre un peu plus facile à suivre, ainsi qu'un exemple de fonction de flèche pour éviter que cette question ne soit fermée en tant que doublon du doublon proposé nouvellement publié. Si vous pensez que mes modifications sont trop éloignées de l'intention initiale de votre réponse, ou sont d'une autre manière inappropriées, n'hésitez pas à les annuler.
4
la révision 4 de cette réponse est en cours de discussion sur meta
bjb568
82

Lorsque vous utilisez doSomething(data, myDiv), vous appelez en fait la fonction et ne faites pas de référence à elle.

Vous pouvez soit passer la doStomethingfonction directement, mais vous devez vous assurer qu'elle a la signature correcte.

Si vous voulez continuer à faire quelque chose comme ça, vous pouvez envelopper son appel dans une fonction anonyme.

function clicked() {
    var myDiv = $("#my-div");
    $.post("someurl.php",someData, function(data){ 
      doSomething(data, myDiv)
    },"json"); 
}

function doSomething(curData, curDiv) {
    ...
}

Dans le code de fonction anonyme, vous pouvez utiliser les variables définies dans la portée englobante. C'est ainsi que fonctionne la portée de Javascript.

Vincent Robert
la source
9
C'est la meilleure solution OMI, pour plus de clarté. Aucune return function(...){...}fonction supplémentaire dans la doSomethingfonction. J'ai trouvé un autre exemple clair du même concept ici: theelitist.net/…
William Denniss
4
Je pense qu'il y a un problème avec celui-ci. Si vos données supplémentaires (myDiv dans ce cas) changent avant le rappel, vous obtenez la nouvelle valeur et non l'ancienne! Dans mon cas, je tirais des appels ajax sur des éléments d'un tableau, et au moment où le premier appel success () a été exécuté, il pensait qu'il avait réussi sur le dernier élément quand il était le premier! La réponse de Bradhouse a fonctionné pour moi.
Chris
@Chris Oui, les variables capturées peuvent être modifiées en Javascript. Si vous ne souhaitez pas ce comportement, vous devez créer une fonction explicite pour capturer la valeur. L'idiome le plus courant est une fonction auto-exécutable (function(myDiv){ ... })(myDiv)pour capturer la variable myDiv. Ceci est implicitement fait dans la réponse @bradhouse.
Vincent Robert
Si vous appelez cela ajax n fois, cette méthode crée n fonctions différentes et les transmet au succès du rappel. C'est mauvais en termes de performances. Refuser cette réponse de @zeroasterisk est bon.
yılmaz
Je suis d'accord avec @WilliamDenniss, à mon humble avis, c'est la meilleure solution, simple et claire
sebasira
52

C'est en fait plus facile que tout le monde le fait sonner ... surtout si vous utilisez la $.ajax({})syntaxe de base par rapport à l'une des fonctions d'assistance.

Passez simplement la key: valuepaire comme vous le feriez sur n'importe quel objet, lorsque vous configurez votre demande ajax ... (car $(this)n'ayant pas encore changé de contexte, c'est toujours le déclencheur de l'appel de liaison ci-dessus)

<script type="text/javascript">
$(".qty input").bind("keypress change", function() {
    $.ajax({
        url: "/order_items/change/"+$(this).attr("data-order-item-id")+"/qty:"+$(this).val()+"/returnas.json",
        type: "POST",
        dataType: "json",
        qty_input: $(this),
        anything_else_i_want_to_pass_in: "foo",
        success: function(json_data, textStatus, jqXHR) {
            /* here is the input, which triggered this AJAX request */
            console.log(this.qty_input);
            /* here is any other parameter you set when initializing the ajax method */
            console.log(this.anything_else_i_want_to_pass_in);
        }
    });
});
</script>

L'une des raisons pour lesquelles cela vaut mieux que de définir la var, c'est que la var est globale et en tant que telle, écrasable ... si vous avez 2 choses qui peuvent déclencher des appels ajax, vous pourriez en théorie les déclencher plus rapidement que l'appel ajax ne répond, et vous auriez la valeur pour le deuxième appel passé dans le premier. En utilisant cette méthode, ci-dessus, cela ne se produirait pas (et c'est assez simple à utiliser aussi).

zeroasterisk
la source
3
Ceci est exactement ce que je cherchais. Je ne savais pas que lorsque vous avez spécifié le type de données comme json, il a automatiquement analysé la réponse pour vous. Cette belle (:
Rooster
1
Cela semble être la meilleure façon de passer des paramètres supplémentaires pour me rappeler. Je serais intéressé si quelqu'un voit un inconvénient à cette approche? Le plus simple et le plus élégant. Merci!
Jan Zyka
" le var est global et en tant que tel, écrasable ". Ce n'est pas si vous le déclarez dans une fonction, qui est également beaucoup plus lisible que la solution ci-dessus. L'utilisation de fermetures est une approche plus propre - voir ma réponse ici: stackoverflow.com/a/18570951/885464
Lorenzo Polidori
5
@ LorenzoPolidori Je ne suis pas d'accord, je trouve l'approche des paramètres supplémentaires beaucoup plus lisible.
Andrew Plummer
2
C'est absolument génial. Comment se fait-il que je n'ai jamais vu ça avant. Je n'ai pas besoin, closurej'en ai juste besoin pour travailler et c'est absolument parfait, propre, simple et non global.
Piotr Kula
21

Dans le monde d'aujourd'hui, il existe une autre réponse qui est plus propre et tirée d'une autre réponse Stack Overflow:

function clicked()
{
    var myDiv = $( "#my-div" );

    $.post( "someurl.php", {"someData": someData}, $.proxy(doSomething, myDiv), "json" );
}

function doSomething( data )
{
    // this will be equal to myDiv now. Thanks to jQuery.proxy().
    var $myDiv = this;

    // doing stuff.
    ...
}

Voici la question et la réponse d'origine: jQuery COMMENT FAIRE ?? passer des paramètres supplémentaires pour réussir le rappel pour l'appel $ .ajax?

b01
la source
8

Vous pouvez également essayer quelque chose comme ceci:

function clicked() {

    var myDiv = $("#my-div");

    $.post("someurl.php",someData,function(data){
        doSomething(data, myDiv);
    },"json"); 
}

function doSomething(curData, curDiv) {

}
Rohan Almeida
la source
5

Vous pouvez utiliser une fermeture de JavaScript:

function wrapper( var1, var2,....) // put here your variables
{
  return function( data, status)
  {
     //Handle here results of call
  }
};

et quand vous pouvez faire:

$.post("someurl.php",data,wrapper(var1, var2, etc...),"html");
Artem Barger
la source
3

J'ai fait une erreur dans le dernier mon post. C'est un exemple de travail pour savoir comment passer un argument supplémentaire dans la fonction de rappel:

function custom_func(p1,p2) {
    $.post(AJAX_FILE_PATH,{op:'dosomething',p1:p1},
        function(data){
            return function(){
                alert(data);
                alert(p2);
            }(data,p2)
        }
    );
    return false;
}
Igor
la source
1
Vous devez soit modifier votre réponse d'origine, soit la supprimer avant d'en ajouter une nouvelle.
Blazemonger
3

Allons simple! :)

$.ajax({
    url: myUrl,
    context: $this, // $this == Current $element
    success: function(data) {
        $.proxy(publicMethods.update, this)(data); // this == Current $element
    }
});
molokoloco
la source
2

Une solution plus générale pour envoyer des requêtes asynchrones à l'aide de l' .ajax()API jQuery et des fermetures pour transmettre des paramètres supplémentaires à la fonction de rappel:

function sendRequest(method, url, content, callback) {
    // additional data for the callback
    var request = {
        method: method,
        url: url
    };

    $.ajax({
        type: method,
        url: url,
        data: content
     }).done(function(data, status, xhr) {
        if (callback) callback(xhr.status, data, request);
     }).fail(function(xhr, status) {
        if (callback) callback(xhr.status, xhr.response, request);
     });
};
Lorenzo Polidori
la source
1

Pour moi, et pour les autres débutants qui viennent de contacter Javascript,
je pense que Closeure Solutionc'est un peu trop déroutant.

Bien que je trouve cela, vous pouvez facilement passer autant de paramètres que vous le souhaitez à chaque rappel ajax en utilisant jquery.

Voici deux solutions plus simples .

Premier, dont @zeroasterisk a mentionné ci-dessus, exemple:

var $items = $('.some_class');
$.each($items, function(key, item){
    var url = 'http://request_with_params' + $(item).html();
    $.ajax({
        selfDom     : $(item),
        selfData    : 'here is my self defined data',

        url         : url,
        dataType    : 'json',
        success     : function(data, code, jqXHR){
            // in $.ajax callbacks, 
            // [this] keyword references to the options you gived to $.ajax
            // if you had not specified the context of $.ajax callbacks.
            // see http://api.jquery.com/jquery.ajax/#jQuery-ajax-settings context
            var $item = this.selfDom;
            var selfdata = this.selfData;
            $item.html( selfdata );
            ...
        } 
    });
});

Deuxièmement, passez des données auto-définies en les ajoutant à celles XHR object qui existent dans toute la durée de vie de ajax-request-response.

var $items = $('.some_class');
$.each($items, function(key, item){
    var url = 'http://request_with_params' + $(item).html();
    $.ajax({
        url         : url,
        dataType    : 'json',
        beforeSend  : function(XHR) {
            // 为了便于回调,把当前的 jquery对象集存入本次 XHR
            XHR.selfDom = $(item);
            XHR.selfData = 'here is my self defined data';
        },
        success     : function(data, code, jqXHR){
            // jqXHR is a superset of the browser's native XHR object
            var $item = jqXHR.selfDom;
            var selfdata = jqXHR.selfData;
            $item.html( selfdata );
            ...
        } 
    });
});

Comme vous pouvez le voir, ces deux solutions ont un inconvénient: vous avez besoin d'écrire un peu plus de code à chaque fois que d'écrire simplement:

$.get/post (url, data, successHandler);

En savoir plus sur $ .ajax: http://api.jquery.com/jquery.ajax/

lyfing
la source
1

Si quelqu'un vient encore ici, voici mon avis:

$('.selector').click(myCallbackFunction.bind({var1: 'hello', var2: 'world'}));

function myCallbackFunction(event) {
    var passedArg1 = this.var1,
        passedArg2 = this.var2
}

Ce qui se passe ici, après la liaison à la fonction de rappel, il sera disponible dans la fonction en tant que this.

Cette idée vient de la façon dont React utilise la bindfonctionnalité.

Kőhalmy Zoltán
la source
1
$(document).on('click','[action=register]',function(){
    registerSocket(registerJSON(),registerDone,second($(this)));
});

function registerSocket(dataFn,doneFn,second){
                 $.ajax({
                       type:'POST',
                       url: "http://localhost:8080/store/public/register",
                       contentType: "application/json; charset=utf-8",
                       dataType: "json",
                       data:dataFn
                 }).done ([doneFn,second])
                   .fail(function(err){
                            console.log("AJAX failed: " + JSON.stringify(err, null, 2));
                        });
}

function registerDone(data){
    console.log(JSON.stringify(data));
}
function second(element){
    console.log(element);
}

Voie secondaire:

function socketWithParam(url,dataFn,doneFn,param){
  $.ajax({
    type:'POST',
    url:url,
    contentType: "application/json; charset=utf-8",
    headers: { 'Authorization': 'Bearer '+localStorage.getItem('jwt')},
    data:dataFn
    }).done(function(data){
      doneFn(data,param);
    })
    .fail(function(err,status,xhr){
    console.log("AJAX failed: " + JSON.stringify(err, null, 2));
    });
}

$(document).on('click','[order-btn]',function(){
  socketWithParam(url,fakeDataFn(),orderDetailDone,secondParam);
});

function orderDetailDone(data,param){
  -- to do something --
}
Musa
la source
0

en fait, votre code ne fonctionne pas car lorsque vous écrivez:

$.post("someurl.php",someData,doSomething(data, myDiv),"json"); 

vous placez un appel de fonction comme troisième paramètre plutôt qu'une référence de fonction .

jrharshath
la source
0

En complément de la réponse de b01 , le deuxième argument de $.proxyest souvent utilisé pour conserver la thisréférence. Les arguments supplémentaires passés à $.proxysont partiellement appliqués à la fonction, en la préremplissant de données. Notez que tous les arguments $.postpassés au rappel seront appliqués à la fin, donc doSomethingdevraient avoir ceux à la fin de sa liste d'arguments:

function clicked() {
    var myDiv = $("#my-div");
    var callback = $.proxy(doSomething, this, myDiv);
    $.post("someurl.php",someData,callback,"json"); 
}

function doSomething(curDiv, curData) {
    //"this" still refers to the same "this" as clicked()
    var serverResponse = curData;
}

Cette approche permet également de lier plusieurs arguments au rappel:

function clicked() {
    var myDiv = $("#my-div");
    var mySpan = $("#my-span");
    var isActive = true;
    var callback = $.proxy(doSomething, this, myDiv, mySpan, isActive);
    $.post("someurl.php",someData,callback,"json"); 
}

function doSomething(curDiv, curSpan, curIsActive, curData) {
    //"this" still refers to the same "this" as clicked()
    var serverResponse = curData;
}
hyperslug
la source