jQuery chuyển nhiều tham số hơn vào cuộc gọi lại


255

Có cách nào để truyền thêm dữ liệu vào hàm gọi lại trong jQuery không?

Tôi có hai hàm và tôi muốn gọi lại $.post, ví dụ, truyền cả dữ liệu kết quả của cuộc gọi AJAX, cũng như một vài đối số tùy chỉnh

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

}

Tôi muốn có thể chuyển các tham số của riêng mình cho một cuộc gọi lại, cũng như kết quả được trả về từ cuộc gọi AJAX.


14
Điều đáng nói là jQuery.ajax () đã có cài đặt ngữ cảnh kể từ phiên bản 1.4 ( jquery14.com/day-01/jquery-14 ) xem ví dụ về cách sử dụng của nó tại đây: stackoverflow.com/questions/5097191/ajax-context- tùy chọn /
Lôi

Tôi đã giải quyết vấn đề của mình khi trả lại dữ liệu sau khi AJAX hoàn thành và sau đó tôi làm gì đó.
Onaiggac

Câu trả lời:


340

Giải pháp là sự ràng buộc của các biến thông qua việc đóng.


Như một ví dụ cơ bản hơn, đây là một hàm ví dụ nhận và gọi một hàm gọi lại, cũng như một hàm gọi lại ví dụ:

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

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

Điều này gọi lại cuộc gọi và cung cấp một đối số duy nhất. Bây giờ bạn muốn cung cấp một đối số bổ sung, vì vậy bạn kết thúc cuộc gọi lại trong bao đóng.

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

Hoặc, đơn giản hơn là sử dụng Hàm mũi tên ES6 :

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

Đối với ví dụ cụ thể của bạn, tôi chưa sử dụng .posthàm trong jQuery, nhưng việc quét nhanh tài liệu cho thấy cuộc gọi lại phải là một con trỏ hàm có chữ ký sau:

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

Do đó tôi nghĩ giải pháp như sau:

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

Chuyện gì đang xảy ra vậy?

Trong dòng cuối cùng, doSomething(extraStuff)được gọi và kết quả của lời gọi đó là một con trỏ hàm .

Bởi vì extraStuffđược truyền dưới dạng đối số cho doSomethingnó nằm trong phạm vi của doSomethinghàm.

Khi extraStuffđược tham chiếu trong hàm bên trong ẩn danh được trả về của doSomethingnó bị ràng buộc bởi việc đóng với extraStuffđối số của hàm ngoài . Điều này đúng ngay cả sau khi doSomethingđã trở lại.

Tôi đã không kiểm tra những điều trên, nhưng tôi đã viết mã rất giống nhau trong 24 giờ qua và nó hoạt động như tôi đã mô tả.

Tất nhiên, bạn có thể vượt qua nhiều biến thay vì một đối tượng 'ExtraStuff' tùy thuộc vào tiêu chuẩn mã hóa / sở thích cá nhân của bạn.


Mục đích của 'var doS Something =' là gì? Điều này khác với việc chỉ khai báo doS Something như một hàm (tức là hàm doS Something (...) {})
Dave Neeley

7
Đo không phải sự thật. Quy tắc phạm vi khác nhau áp dụng cho hai khai báo chức năng. Hành vi khác nhau rất nhiều tùy thuộc vào thời gian chạy JS (đọc trình duyệt). Ngoài ra, hãy thử so sánh tên hàm được hiển thị trong stacktrace / breakpoint trong Fireorms.
Ihor Kaharlichenko

1
Ihor: Bạn hoàn toàn chính xác về sự khác biệt giữa hai kiểu khai báo. Một lời giải thích tốt có ở đây: stackoverflow.com/questions/336859/ từ
bradhouse

2
Điều này có thể hơi khó theo dõi đối với người dùng đang cố gắng áp dụng điều này cho các vấn đề chung hơn do tính đặc thù của ví dụ. Tôi đã thêm một ví dụ đơn giản hóa để dễ theo dõi hơn một chút, cũng như ví dụ về chức năng mũi tên để ngăn câu hỏi này được đóng lại dưới dạng bản sao của bản sao được đề xuất mới được đăng. Nếu bạn cảm thấy các chỉnh sửa của tôi đi quá xa so với mục đích ban đầu của câu trả lời của bạn hoặc theo bất kỳ cách nào khác không phù hợp, xin vui lòng cuộn lại.

4
bản sửa đổi 4 của câu trả lời này đang được thảo luận trên meta
bjb568

82

Khi sử dụng doSomething(data, myDiv), bạn thực sự gọi hàm và không tham chiếu đến nó.

Bạn có thể truyền doStomethingtrực tiếp chức năng nhưng bạn phải đảm bảo nó có chữ ký chính xác.

Nếu bạn muốn giữ doSthing theo cách của nó, bạn có thể kết thúc cuộc gọi của nó trong một chức năng ẩn danh.

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

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

Bên trong mã hàm ẩn danh, bạn có thể sử dụng các biến được xác định trong phạm vi kèm theo. Đây là cách hoạt động của phạm vi Javascript.


9
Đây là giải pháp tốt nhất IMO, vì mục đích rõ ràng. Không có return function(...){...}chức năng bổ sung trong doSomethingchức năng. Tôi tìm thấy một ví dụ rõ ràng khác về khái niệm tương tự ở đây: theelitist.net/ Kẻ
William Denniss

4
Tôi nghĩ rằng có một vấn đề với cái này. Nếu dữ liệu bổ sung của bạn (myDiv trong trường hợp này) thay đổi trước khi cuộc gọi lại kích hoạt, bạn sẽ nhận được giá trị mới chứ không phải giá trị cũ! Trong trường hợp của tôi, tôi đã thực hiện các cuộc gọi ajax trên các mục trong một mảng và đến khi cuộc gọi thành công () đầu tiên được thực hiện, nó nghĩ rằng nó đã thành công ở mục cuối cùng khi nó là lần đầu tiên! câu trả lời của bradhouse làm việc cho tôi.
Chris

@Chris Có, các biến bị bắt có thể được thay đổi trong Javascript. Nếu bạn không muốn hành vi này, bạn cần tạo một hàm rõ ràng để nắm bắt giá trị. Thành ngữ thông thường nhất là một hàm tự thực hiện (function(myDiv){ ... })(myDiv)để nắm bắt biến myDiv. Điều này được thực hiện ngầm trong câu trả lời @bradhouse.
Vincent Robert

Nếu bạn gọi đây là ajax n lần, phương thức này tạo ra n hàm khác nhau và chuyển chúng đến gọi lại thành công. Điều này là xấu về sự không hoàn hảo. Để kiềm chế câu trả lời của @zeroasterisk là tốt.
yılmaz

Tôi đồng ý với @WilliamDenniss, IMHO đây là giải pháp tốt nhất, đơn giản và rõ ràng
sebasira

52

Nó thực sự dễ hơn mọi người phát ra âm thanh ... đặc biệt nếu bạn sử dụng $.ajax({})cú pháp cơ sở so với một trong các hàm trợ giúp.

Chỉ cần chuyển qua key: valuecặp như bạn làm với bất kỳ đối tượng nào, khi bạn thiết lập yêu cầu ajax của mình ... (vì $(this)chưa thay đổi ngữ cảnh, nó vẫn là trình kích hoạt cho lệnh gọi liên kết ở trên)

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

Một trong những lý do này tốt hơn so với cài đặt var, đó là var là toàn cục và do đó, ghi đè ... nếu bạn có 2 điều có thể kích hoạt các cuộc gọi ajax, về lý thuyết bạn có thể kích hoạt chúng nhanh hơn phản hồi cuộc gọi ajax và bạn sẽ có giá trị cho cuộc gọi thứ hai được chuyển vào cuộc gọi đầu tiên. Sử dụng phương pháp này, ở trên, điều đó sẽ không xảy ra (và nó cũng khá đơn giản để sử dụng).


3
điều này thật đúng với gì mà tôi đã tìm kiếm. Tôi không biết khi bạn chỉ định dataType là json, nó sẽ tự động phân tích cú pháp phản hồi cho bạn. Thật tuyệt (:
Dậu

1
Âm thanh như cách tốt nhất để vượt qua các thông số bổ sung để gọi lại cho tôi. Tôi sẽ quan tâm nếu bất cứ ai nhìn thấy một nhược điểm với phương pháp này? Dễ nhất và thanh lịch nhất. Cảm ơn!
Jan Zyka

" var là toàn cầu và như vậy, ghi đè ". Không phải là nếu bạn khai báo nó trong một hàm, nó cũng dễ đọc hơn nhiều so với giải pháp ở trên. Sử dụng bao đóng là một cách tiếp cận sạch hơn - xem câu trả lời của tôi ở đây: stackoverflow.com/a/18570951/885464
Lorenzo Polidori

5
@LorenzoPolidori Tôi không đồng ý, tôi thấy cách tiếp cận tham số bổ sung dễ đọc hơn nhiều.
Andrew Plummer

2
Điều này là hoàn toàn xuất sắc. Tại sao tôi không bao giờ thấy điều này trước đây. Tôi không cần closuretôi chỉ cần nó để làm việc và điều này hoàn toàn là ace, sạch sẽ, đơn giản và không toàn cầu.
Piotr Kula

21

Trong thế giới ngày nay, có một câu trả lời khác sạch hơn và được lấy từ một câu trả lời Stack Overflow khác:

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

Đây là câu hỏi và câu trả lời ban đầu: jQuery LÀM THẾ NÀO ?? vượt qua các tham số bổ sung để gọi lại thành công cho cuộc gọi $ .ajax?


8

Bạn cũng có thể thử một cái gì đó như sau:

function clicked() {

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

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

function doSomething(curData, curDiv) {

}

5

Bạn có thể sử dụng việc đóng JavaScript:

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

và khi bạn có thể làm:

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

3

Tôi đã phạm một lỗi trong bài viết cuối cùng của tôi. Đây là ví dụ hoạt động cho cách truyền đối số bổ sung trong hàm gọi lại:

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
Bạn nên chỉnh sửa câu trả lời gốc của mình hoặc xóa nó trước khi thêm câu trả lời mới.
Blazemonger

3

Hãy đi đơn giản! :)

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

2

Một giải pháp tổng quát hơn để gửi các yêu cầu không đồng bộ bằng .ajax()API jQuery và đóng để truyền các tham số bổ sung cho hàm gọi lại:

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

Đối với tôi và những người mới khác vừa liên hệ với Javascript,
tôi nghĩ rằng đó Closeure Solutionlà một loại quá khó hiểu.

Trong khi tôi thấy rằng, bạn có thể dễ dàng vượt qua nhiều tham số như bạn muốn cho mỗi cuộc gọi lại ajax bằng cách sử dụng jquery.

Đây là hai giải pháp dễ dàng hơn .

Đầu tiên, mà @zeroasterisk đã đề cập ở trên, ví dụ:

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

Thứ hai, vượt qua các dữ liệu tự xác định bằng cách thêm chúng vào dữ liệu XHR object tồn tại trong toàn bộ vòng đời ajax-request-answer.

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

Như bạn có thể thấy hai giải pháp này có một nhược điểm là: bạn cần viết nhiều mã hơn mỗi lần thay vì chỉ viết:

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

Đọc thêm về $ .ajax: http://api.jquery.com/jquery.ajax/


1

Nếu ai đó vẫn đến đây, đây là của tôi:

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

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

Điều gì xảy ra ở đây, sau khi liên kết với chức năng gọi lại, nó sẽ có sẵn trong chức năng như this.

Ý tưởng này xuất phát từ cách React sử dụng bindchức năng.


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

Cách thứ hai:

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

thực tế, mã của bạn không hoạt động vì khi bạn viết:

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

bạn thực hiện một cuộc gọi hàm làm tham số thứ ba chứ không phải là tham chiếu hàm .


0

Là một phụ lục cho câu trả lời của b01 , đối số thứ hai $.proxythường được sử dụng để bảo toàn thistham chiếu. Các đối số bổ sung được chuyển đến $.proxyđược áp dụng một phần cho hàm, điền trước nó với dữ liệu. Lưu ý rằng bất kỳ đối số nào được $.postchuyển đến cuộc gọi lại sẽ được áp dụng ở cuối, do đó, doSomethingnên có những đối số ở cuối danh sách đối số của nó:

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

Cách tiếp cận này cũng cho phép nhiều đối số được liên kết với hàm gọi lại:

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;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.