web-dev-qa-db-fra.com

jQuery passe plus de paramètres dans le rappel

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

J'ai deux fonctions et je veux que le rappel au $.post, par exemple, transmette à 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.

251
atp

La solution est la liaison des variables par la fermeture.


Comme exemple plus simple, 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, afin de boucler le rappel dans la fermeture.

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

Ou, plus simplement, en utilisant ES6 Arrow Functions :

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

En ce qui concerne votre exemple spécifique, je n’ai pas utilisé la fonction .post dans jQuery, mais une analyse rapide de la documentation suggère que le rappel doit être un pointeur de fonction avec la signature suivante:

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

Par conséquent, je pense 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.

Étant donné que extraStuff est passé en tant qu'argument à doSomething, il fait partie de la portée de la fonction doSomething.

Lorsque extraStuff est référencé dans la fonction interne anonyme retournée de doSomething, il est lié par la fermeture à l'argument extraStuff de la fonction externe. Cela est vrai même après le retour de doSomething.

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.

336
bradhouse

Lorsque vous utilisez doSomething(data, myDiv), vous appelez la fonction et ne vous y référez pas.

Vous pouvez soit passer directement la fonction doStomething, mais vous devez vous assurer que la signature est correcte.

Si vous voulez garder 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 la façon dont la portée de Javascript fonctionne.

80
Vincent Robert

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

Il vous suffit de passer la paire key: value comme vous le feriez pour n’importe quel objet, lorsque vous configurerez votre demande ajax ... car $(this) n’a pas encore changé de contexte, c’est toujours le déclencheur de la requête ajax. lier appel 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>

Une des raisons pour laquelle cela vaut mieux que de définir la var, 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 pouvez en théorie les déclencher plus rapidement que ne répond un appel ajax, 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).

50
zeroasterisk

Dans le monde actuel, il existe une autre réponse plus propre, 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 POUVOIR: transmettre des paramètres supplémentaires pour un rappel de réussite pour un appel $ .ajax?

20
b01

Vous pouvez également essayer quelque chose comme ce qui suit:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}
7
Rohan Almeida

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");
5
Artem Barger

Allons simple! :)

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

J'ai fait une erreur dans le dernier post. Ceci est un exemple de travail pour savoir comment passer un argument supplémentaire dans une 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;
}
3
Igor

Une solution plus générale d’envoi de requêtes asynchrones à l’aide de l’API jQuery .ajax() et des fermetures permettant de 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);
     });
};
2
Lorenzo Polidori

Si quelqu'un vient encore ici, voici ce que je pense:

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

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

Que se passe-t-il ici, après la liaison à la fonction de rappel, elle sera disponible dans la fonction en tant que this.

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

1
Kőhalmy Zoltán
$(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 --
}
1
Musa

Pour moi, et les débutants qui viennent de contacter avec Javascript,
Je pense que le Closeure Solution est un peu trop déroutant.

Bien que j’ai trouvé cela, vous pouvez facilement passer autant de paramètres que vous le souhaitez à chaque rappel ajax utilisant jquery.

Voici deux solutions plus simples .

Le premier, qui @ 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, transmettez des données auto-définies en les ajoutant dans le XHR object qui existe 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 constater, ces deux solutions présentent les inconvénients suivants: vous avez besoin d'écrire un peu plus de code à chaque fois que d'écrire:

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

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

1
lyfing

En tant qu'addendum à réponse de b01 , le deuxième argument de $.proxy est souvent utilisé pour conserver la référence this. Les arguments supplémentaires passés à $.proxy sont partiellement appliqués à la fonction, la remplissant préalablement de données. Notez que tous les arguments $.post passés au rappel seront appliqués à la fin, donc doSomething devrait 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;
}
0
hyperslug

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'un référence de fonction.

0
jrharshath