jQuery передає більше параметрів у зворотний виклик


255

Чи є спосіб передати більше даних у функцію зворотного дзвінка у jQuery?

У мене є дві функції, і я хочу, щоб зворотний виклик $.post, наприклад, передав як отримані дані виклику AJAX, так і кілька спеціальних аргументів

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

}

Я хочу мати можливість передати свої власні параметри зворотній дзвінок, а також результат, повернутий при виклику AJAX.


14
Варто згадати, що jQuery.ajax () має контекстну настройку з версії 1.4 ( jquery14.com/day-01/jquery-14 ) див. Приклад її використання тут: stackoverflow.com/questions/5097191/ajax-context- варіант /…
russau

Я вирішив свою проблему з поверненням даних після завершення AJAX, і тоді я щось роблю.
Onaiggac

Відповіді:


340

Рішенням є зв'язування змінних через закриття.


Як більш базовий приклад, ось приклад функції, яка приймає та викликає функцію зворотного дзвінка, а також приклад функції зворотного виклику:

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

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

Це викликає зворотний виклик і надає один аргумент. Тепер ви хочете надати додатковий аргумент, тому ви завершуєте зворотний виклик.

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

Або, простіше, використовуючи функції стрілок ES6 :

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

Що стосується вашого конкретного прикладу, я не використовував цю .postфункцію в jQuery, але швидке сканування документації дозволяє припустити, що зворотний дзвінок повинен бути покажчиком функції із такою підписом:

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

Тому я думаю, що рішення таке:

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

Що відбувається?

В останньому рядку, doSomething(extraStuff)буде викликаний і результатом цього виклику є покажчиком на функцію .

Оскільки extraStuffпередається як аргумент, doSomethingвін знаходиться в межах doSomethingфункції.

Коли на extraStuffнього посилається повернута анонімна внутрішня функція, doSomethingвона пов'язана із закриттям на extraStuffаргумент зовнішньої функції . Це вірно навіть після того, doSomethingяк повернувся.

Я не перевіряв вищезазначене, але я написав дуже подібний код за останні 24 години, і він працює так, як я описав.

Звичайно, ви можете передавати кілька змінних замість одного об'єкта 'extraStuff' залежно від ваших особистих уподобань / стандартів кодування.


Яка мета 'var doSomething ='? Чим це відрізняється від просто декларування doSomething як функції (тобто функції doSomething (...) {})
Дейв Нілі

7
Це не правда. До цих двох декларацій функцій застосовуються різні правила сфери застосування. Поведінка сильно відрізняється залежно від часу виконання JS (читання браузера). Спробуйте також порівняти ім'я функції, показане у стеці / відключенні в Firebug.
Ігор Кахарліченко

1
Ігор: Ви абсолютно правильні щодо різниці між двома стилями декларування. Гарне пояснення тут: stackoverflow.com/questions/336859 / ...
bradhouse

2
Користувачам, які намагаються застосувати це до більш загальних проблем, через специфіку прикладу може бути дещо складно. Я додав спрощений приклад, який полегшує наслідування, а також приклад функції стрілки, щоб запобігти закриттю цього питання як дубліката щойно опублікованого запропонованого дубліката. Якщо ви вважаєте, що мої зміни занадто далеко відхиляються від початкового наміру вашої відповіді, або будь-яким іншим способом недоречним, будь ласка, не соромтеся відкотити їх.


82

Під час використання doSomething(data, myDiv)ви фактично викликаєте функцію і не робите посилання на неї.

Ви можете передати doStomethingфункцію безпосередньо, але ви повинні переконатися, що вона має правильну підпис.

Якщо ви хочете зберегти doSomething таким, яким він є, ви можете перетворити його виклик в анонімну функцію.

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

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

Всередині анонімного коду функції ви можете використовувати змінні, визначені в області, що додається. Ось так працює масштабування Javascript.


9
Це найкраще рішення ІМО, задля ясності. Немає додаткової return function(...){...}функції в межах doSomethingфункції. Тут я знайшов ще один чіткий приклад тієї самої концепції: theelitist.net/…
Вільям Денніс,

4
Я думаю, що в цьому є проблема. Якщо ваші додаткові дані (myDiv в даному випадку) змінюються до початку зворотного виклику, ви отримуєте нове значення, а не старе! У моєму випадку я знімав ajax-дзвінки на елементи в масиві, і до того моменту, коли був виконаний перший виклик успіху (), він вважав, що він досяг успіху в останньому елементі, коли він був першим! Відповідь Брадхауса спрацювала на мене.
Кріс

@Chris Так, захоплені змінні можуть бути змінені в Javascript. Якщо ви не хочете такої поведінки, вам потрібно створити чітку функцію для збору значення. Найбільш звичайна ідіома - це функція самовиконання (function(myDiv){ ... })(myDiv)для захоплення змінної myDiv. Це неявно робиться у відповіді @bradhouse.
Вінсент Роберт

Якщо ви називаєте цей ajax n разів, цей метод створює n різних функцій і передає їх на успішний зворотний виклик. Це погано в плані виконання. Утриматись відповідь на цю @zeroasterisk добре.
yılmaz

Я погоджуюся з @WilliamDenniss, IMHO, це найкраще, просте і зрозуміле рішення
sebasira

52

Це насправді простіше, ніж всі звучать ... особливо якщо ви використовуєте $.ajax({})базовий синтаксис проти однієї з хелперних функцій.

Просто перейдіть у key: valueпару, як і на будь-який об’єкт, коли ви налаштовуєте свій запит на ajax ... (оскільки $(this)ще не змінився контекст, він все ще є тригером для прив’язки виклику вище)

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

Однією з причин, чому це краще, ніж встановити var, є те, що var є глобальним і як такий, перезаписуваним ... якщо у вас є дві речі, які можуть викликати виклики Ajax, теоретично ви могли б викликати їх швидше, ніж відповідь на виклик Ajax, і ви мали б значення для другого дзвінка, переданого в перший. Використовуючи цей метод вище, цього не відбудеться (і його досить просто використовувати).


3
це саме те, що я шукав. Я не знав, коли ви вказали dataType як json, він автоматично розбирав відповідь для вас. Що приємно (:
Півень

1
Здається, найкращий спосіб передавати мені додаткові параметри для зворотного дзвінка. Мені буде цікаво, якщо хтось бачить недолік при такому підході? Найпростіше і найелегантніше. Дякую!
Jan Zyka

" var є глобальним і як такий, можливим перезаписом ". Це не якщо ви оголосите це в межах функції, яка також набагато читає, ніж рішення вище. Використання закриття - більш чистий підхід - дивіться мою відповідь тут: stackoverflow.com/a/18570951/885464
Лоренцо Полідорі

5
@LorenzoPolidori Я не погоджуюся, я вважаю, що підхід додаткового параметра набагато легше читається.
Ендрю Пламмер

2
Це абсолютно геніально. Як я ніколи цього не бачив. Мені це не потрібно, closureя просто потребую, щоб він працював, і це абсолютно туз, чистий, простий і не глобальний.
Пьотр Кула

21

У сучасному світі є ще одна відповідь, яка є більш чистою, і взята з іншої відповіді на переповнення стека:

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

Ось оригінальне запитання та відповідь: jQuery КАК ?? передавати додаткові параметри до успішного зворотного виклику для виклику $ .ajax?


8

Ви також можете спробувати щось подібне:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}

5

Ви можете використовувати закриття JavaScript:

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

і коли ви можете:

$.post("someurl.php",data,wrapper(var1, var2, etc...),"html");

3

Я помилився в останньому своєму пості. Це робочий приклад для передачі додаткового аргументу у функції зворотного виклику:

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

1
Вам слід відредагувати оригінальну відповідь або видалити її, перш ніж додати нову.
Blazemonger

3

Ходімо просто! :)

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

2

Більш загальне рішення для надсилання асинхронних запитів за допомогою .ajax()API jQuery та закриття для передачі додаткових параметрів функції зворотного виклику:

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

1

Для мене та інших новачків, які щойно зв’язалися з Javascript,
я вважаю, що Closeure Solutionце занадто заплутано.

Поки я виявив це, ви можете легко передавати стільки параметрів, скільки вам потрібно, до кожного зворотного виклику Ajax за допомогою jquery.

Ось два простіших рішення .

Перший, про який згадував @zeroasterisk , наприклад:

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

По-друге, передайте самостійно визначені дані, додавши їх у ті, XHR object що існують у цілому періоді життя ajax-запит-відповідь.

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

Як ви бачите, ці два рішення мають недолік, який: вам потрібно писати трохи більше коду кожного разу, ніж просто писати:

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

Детальніше про $ .ajax: http://api.jquery.com/jquery.ajax/


1

Якщо хтось все-таки приїжджає сюди, це моя думка:

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

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

Що відбувається тут, після прив’язки до функції зворотного виклику, воно буде доступне як функція як this.

Ця ідея випливає з того, як React використовує bindфункціонал.


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

Вторинний спосіб:

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

0

насправді ваш код не працює, тому що коли ви пишете:

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

ви ставите виклик функції як третій параметр, а не посилання на функцію .


0

Як доповнення до відповіді b01 , другий аргумент $.proxyчасто використовується для збереження thisпосилання. Додані додаткові аргументи $.proxyчастково застосовані до функції, попередньо заповнивши її даними. Зауважте, що будь-які аргументи, що $.postпередаються до зворотного виклику, будуть застосовані в кінці, тому вони doSomethingповинні мати такі в кінці його списку аргументів:

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

Цей підхід також дозволяє прив'язати кілька аргументів до зворотного дзвінка:

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;
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.