jQuery przekazuje więcej parametrów do wywołania zwrotnego

255

Czy istnieje sposób na przekazanie większej ilości danych do funkcji zwrotnej w jQuery?

Mam dwie funkcje i chcę, aby wywołanie zwrotne $.postna przykład przekazało zarówno dane wynikowe wywołania AJAX, jak i kilka niestandardowych argumentów

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) {

}

Chcę mieć możliwość przekazania własnych parametrów do wywołania zwrotnego, a także wyniku zwróconego z wywołania AJAX.

atp
źródło
14
Warto wspomnieć, że jQuery.ajax () ma ustawienie kontekstu od wersji 1.4 ( jquery14.com/day-01/jquery-14 ) patrz przykład jego użycia tutaj: stackoverflow.com/questions/5097191/ajax-context- opcja /…
Russau
Usunąłem problem ze zwróceniem danych po zakończeniu AJAX, a następnie wykonałem coś.
Onaiggac,

Odpowiedzi:

340

Rozwiązaniem jest wiązanie zmiennych poprzez zamknięcie.


Jako bardziej podstawowy przykład, oto przykładowa funkcja, która odbiera i wywołuje funkcję zwrotną, a także przykładową funkcję zwrotną:

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

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

To wywołuje wywołanie zwrotne i podaje pojedynczy argument. Teraz chcesz podać dodatkowy argument, więc zamknij wywołanie zwrotne.

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

Lub, po prostu, używając ES6 Funkcje strzałek :

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

Jeśli chodzi o konkretny przykład, nie użyłem tej .postfunkcji w jQuery, ale szybki skan dokumentacji sugeruje, że wywołanie zwrotne powinno być wskaźnikiem funkcji o następującej sygnaturze:

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

Dlatego myślę, że rozwiązanie jest następujące:

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");
};

Co się dzieje?

W ostatnim wierszu doSomething(extraStuff)jest wywoływany, a wynikiem tego wywołania jest wskaźnik funkcji .

Ponieważ extraStuffjest przekazywany jako argument, należy do doSomethingzakresu doSomethingfunkcji.

Gdy extraStuffjest odwołany w zwróconej anonimowej funkcji wewnętrznej doSomething, jest związany przez zamknięcie extraStuffargumentu funkcji zewnętrznej . Dzieje się tak nawet po doSomethingpowrocie.

Nie testowałem powyższego, ale napisałem bardzo podobny kod w ciągu ostatnich 24 godzin i działa tak, jak to opisałem.

Możesz oczywiście przekazać wiele zmiennych zamiast jednego obiektu „extraStuff” w zależności od osobistych preferencji / standardów kodowania.

Bradhouse
źródło
Jaki jest cel „var doSomething =”? Czym różni się to od zadeklarowania doSomething jako funkcji (tj. Funkcji doSomething (...) {})
Dave Neeley,
7
To nieprawda. Do tych dwóch deklaracji funkcji mają zastosowanie różne reguły zakresu. Zachowanie różni się bardzo w zależności od środowiska wykonawczego JS (odczyt przeglądarki). Spróbuj także porównać nazwę funkcji pokazaną w stacktrace / breakpoint w Firebug.
Ihor Kaharlichenko
1
Ihor: Masz całkowitą rację co do różnicy między dwoma stylami deklaracji. Dobre wyjaśnienie znajduje się tutaj: stackoverflow.com/questions/336859/…
bradhouse
2
Może to być nieco trudne do naśladowania dla użytkowników próbujących zastosować to do bardziej ogólnych problemów ze względu na specyfikę tego przykładu. Dodałem uproszczony przykład, aby ułatwić śledzenie, a także przykład funkcji strzałki, aby zapobiec zamknięciu tego pytania jako duplikatu nowo opublikowanego proponowanego duplikatu. Jeśli uważasz, że moje zmiany zbytnio odbiegają od pierwotnego celu Twojej odpowiedzi lub są w jakikolwiek inny sposób niewłaściwe, możesz je wycofać.
4
wersja 4 tej odpowiedzi jest dyskutowana na stronie meta
bjb568,
82

Podczas używania doSomething(data, myDiv)wywołujesz funkcję i nie odwołujesz się do niej.

Możesz przekazać tę doStomethingfunkcję bezpośrednio, ale musisz upewnić się, że ma poprawny podpis.

Jeśli chcesz zachować doSomething taki, jaki jest, możesz zawrzeć jego wywołanie w funkcji anonimowej.

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

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

Wewnątrz anonimowego kodu funkcji można użyć zmiennych zdefiniowanych w obejmującym zakresie. W ten sposób działa zakres JavaScript.

Vincent Robert
źródło
9
To jest najlepsze rozwiązanie IMO, dla zachowania przejrzystości. Brak dodatkowej return function(...){...}funkcji w ramach doSomethingfunkcji. Znalazłem tutaj inny wyraźny przykład tej samej koncepcji: theelitist.net/…
William Denniss,
4
Myślę, że jest z tym problem. Jeśli dodatkowe dane (w tym przypadku myDiv) ulegną zmianie przed uruchomieniem wywołania zwrotnego, otrzymasz nową wartość, a nie starą! W moim przypadku odpalałem wywołania ajaxowe na elementach w tablicy i zanim wykonano pierwsze wywołanie success (), wydawało się, że udało się na ostatnim elemencie, gdy był pierwszy! odpowiedź bradhouse działała dla mnie.
Chris
@Chris Tak, przechwycone zmienne można zmienić w JavaScript. Jeśli nie chcesz tego zachowania, musisz utworzyć jawną funkcję do przechwytywania wartości. Najczęściej stosowanym idiomem jest samoczynnie wykonująca się funkcja (function(myDiv){ ... })(myDiv)do przechwytywania zmiennej myDiv. Odbywa się to domyślnie w odpowiedzi na @bradhouse.
Vincent Robert
Jeśli wywołasz ten ajax n razy, ta metoda tworzy n różnych funkcji i przekazuje je do wywołania zwrotnego powodzenia. Jest to złe pod względem wydajności. Refrenowanie odpowiedzi @zeroasterisk jest dobre.
yılmaz
Zgadzam się z @WilliamDenniss, IMHO to najlepsze, proste i jasne rozwiązanie
sebasira,
52

W rzeczywistości jest to łatwiejsze niż wszyscy brzmią ... zwłaszcza jeśli używasz $.ajax({})podstawowej składni vs. jednej z funkcji pomocnika.

Po prostu przekaż key: valueparę, tak jak w przypadku dowolnego obiektu, podczas konfigurowania żądania ajax ... (ponieważ $(this)nie zmienił jeszcze kontekstu, nadal jest wyzwalaczem wywołania wiązania powyżej)

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

Jednym z powodów, dla których jest to lepsze niż ustawianie var, jest to, że var jest globalny i jako taki jest nadpisywalny ... jeśli masz 2 rzeczy, które mogą wywoływać wywołania ajax, możesz teoretycznie wywołać je szybciej niż odpowiedzi wywołania ajax, i wartość drugiego połączenia zostanie przekazana do pierwszego. Zastosowanie powyższej metody nie byłoby możliwe (i jest dość proste w użyciu).

zeroasterisk
źródło
3
właśnie tego szukałem. Nie wiedziałem, kiedy określiłeś dataType jako json, automatycznie przeanalizował dla ciebie odpowiedź. To miło (:
Kogut
1
Brzmi jak najlepszy sposób na przekazanie dodatkowych parametrów, aby do mnie oddzwonić. Byłbym zainteresowany, gdyby ktoś widział wadę takiego podejścia? Najłatwiejszy i najbardziej elegancki. Dzięki!
Jan Zyka
var jest globalny i jako taki można go zastąpić ”. Nie dzieje się tak, jeśli zadeklarujesz to w ramach funkcji, która jest również znacznie bardziej czytelna niż powyższe rozwiązanie. Używanie zamknięć jest czystszym podejściem - patrz moja odpowiedź tutaj: stackoverflow.com/a/18570951/885464
Lorenzo Polidori
5
@LorenzoPolidori Nie zgadzam się, uważam, że dodatkowe podejście parametryczne jest znacznie bardziej czytelne.
Andrew Plummer,
2
To jest absolutnie genialne. Dlaczego nigdy wcześniej tego nie widziałem. Nie potrzebuję closure, potrzebuję go tylko do działania, a to absolutnie as, czysty, prosty i nieglobalny.
Piotr Kula,
21

W dzisiejszym świecie istnieje inna odpowiedź, która jest czystsza i zaczerpnięta z innej odpowiedzi Przepełnienie stosu:

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

Oto oryginalne pytanie i odpowiedź: jQuery JAK? przekazać dodatkowe parametry do wywołania zwrotnego dla połączenia $ .ajax?

b01
źródło
8

Możesz także spróbować czegoś takiego:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}
Rohan Almeida
źródło
5

Możesz użyć zamknięcia JavaScript:

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

a kiedy możesz:

$.post("someurl.php",data,wrapper(var1, var2, etc...),"html");
Artem Barger
źródło
3

Popełniłem błąd w ostatnim poście. Oto działający przykład przekazywania dodatkowych argumentów w funkcji zwrotnej:

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
źródło
1
Powinieneś edytować oryginalną odpowiedź lub ją usunąć przed dodaniem nowej.
Blazemonger
3

Przejdźmy prosto! :)

$.ajax({
    url: myUrl,
    context: $this, // $this == Current $element
    success: function(data) {
        $.proxy(publicMethods.update, this)(data); // this == Current $element
    }
});
molokoloco
źródło
2

Bardziej ogólne rozwiązanie do wysyłania żądań asynchronicznych za pomocą .ajax()interfejsu API i zamknięć jQuery w celu przekazania dodatkowych parametrów do funkcji wywołania zwrotnego:

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
źródło
1

Dla mnie i innych początkujących, którzy właśnie skontaktowali się z Javascriptem,
myślę, że Closeure Solutionjest to trochę zbyt mylące.

Chociaż znalazłem to, możesz łatwo przekazać tyle parametrów, ile chcesz do każdego wywołania zwrotnego ajax za pomocą jquery.

Oto dwa łatwiejsze rozwiązania .

Pierwszy, o którym wspominał @zeroasterisk , przykład:

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 );
            ...
        } 
    });
});

Po drugie, przekaż dane zdefiniowane przez siebie, dodając je do tego, XHR object co istnieje w całym okresie życia ajax-żądanie-odpowiedź.

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 );
            ...
        } 
    });
});

Jak widać, te dwa rozwiązania mają tę wadę, że za każdym razem musisz napisać trochę więcej kodu niż tylko napisać:

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

Przeczytaj więcej o $ .ajax: http://api.jquery.com/jquery.ajax/

lyfing
źródło
1

Jeśli ktoś nadal tu przychodzi, oto moje zdanie:

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

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

Co się tutaj stanie, po powiązaniu z funkcją wywołania zwrotnego, będzie ona dostępna w ramach funkcji jako this.

Pomysł pochodzi z tego, jak React wykorzystuje tę bindfunkcjonalność.

Kőhalmy Zoltán
źródło
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);
}

Drugi sposób:

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
źródło
0

tak naprawdę twój kod nie działa, ponieważ kiedy piszesz:

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

umieszczasz wywołanie funkcji jako trzeci parametr zamiast odwołania do funkcji .

jrharshath
źródło
0

Jako dodatek do odpowiedzi b01 drugi argument $.proxyczęsto służy do zachowania thisodniesienia. Dodatkowe przekazane argumenty $.proxysą częściowo stosowane do funkcji, wstępnie wypełniając ją danymi. Zauważ, że wszelkie argumenty $.postprzekazywane do wywołania zwrotnego zostaną zastosowane na końcu, dlatego doSomethingpowinny znajdować się na końcu listy argumentów:

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;
}

Takie podejście pozwala również na powiązanie wielu argumentów z wywołaniem zwrotnym:

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;
}
hiperslug
źródło