[英]Avoiding Ajax Callback Hell “Nesting”
嘗試構建鏈式選擇表單時,當使用.on('change')使用$ .ajax請求進行選擇時,我已經完成了此操作,但是當我需要顯示時,事情開始失去控制根據先前的選擇可能存在或可能不存在的4個以上的下拉選項。 有沒有一種方法可以使用Promise或其他方式重構此代碼。 也許會指出正確的方向? 這是一個示例代碼片段。
$('#model').on('change',function(){
var modelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
if(modelID){
$.ajax({
url:'lib/ajaxData.php',
type: "POST",
data:{
"action" : 'getsubmodels',
"makeset_id" : makesetID,
"yearset_id" : yearsetID,
"model_id" : modelID
},
dataType: "html",
success:function(html){
if (html) {
// found a submodel
$('#submodel').html(html);
$('#submodel').on('change',function(){
var submodelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
var modelsetID = $('#model').val();
$.ajax({
type:'POST',
url:'lib/ajaxData.php',
data: {
"action" : 'getbodytypes',
"year": yearsetID,
"make" : makesetID,
"model" : modelsetID,
"submodel" : submodelID
},
success:function(html){
// found a bodytype
if(html) {
$('#bodytype').html(html);
$('#bodytype').on('change',function(){
//more dropdowns
}
}
}
});
});
}else{
//no submodel
}
}
});
}else{
$('.product').html('Select Some Values');
}
});
編輯以顯示HTML
這是HTML的樣子,除非該選項可用,否則某些下拉菜單將被隱藏。
<div class="select-boxes row">
<div class="small-12 medium-1 columns">
<select name="year" id="year">
<option value="">Year </option>
</select>
</div>
<div class="small-12 medium-1 columns">
<select name="make" id="make">
<option value="">Make</option>
</select>
</div>
<div class="small-12 medium-1 columns end">
<select name="model" id="model">
<option value="">Model</option>
</select>
</div>
<div class="small-12 medium-1 columns end submodel modifier" style="display:none;">
<select name="submodel" id="submodel">
<option value="">Submodel</option>
</select>
</div>
<div class="small-12 medium-1 columns end bodytype modifier" style="display:none;">
<select name="bodytype" id="bodytype">
<option value="">BodyType</option>
</select>
</div>
<div class="small-12 medium-1 columns end enginetype modifier" style="display:none;">
<select name="engine" id="engine">
<option value="">EngineType</option>
</select>
</div>
<div class="small-12 medium-1 columns end drivetype modifier" style="display:none;">
<select name="drive" id="drive">
<option value="">DriveType</option>
</select>
</div>
</div>
更具可讀性
$('#model').on('change', modelOnChange);
function modelOnChange() {
var modelID = $(this).val(),
yearsetID = $('#year').val(),
makesetID = $('#make').val();
(modelID) ? doStuff(modelID, yearsetID, makesetID) : doStuffIfNoId();
}
function doStuff(modelID, yearsetID, makesetID) {
getSubModels(afterGetSubModels);
}
function doStuffIfNoId() {
$('.product').html('Select Some Values');
}
function getSubModels(callback) {
$.ajax({
url : 'lib/ajaxData.php',
type : "POST",
data : {
"action" : 'getsubmodels',
"makeset_id": makesetID,
"yearset_id": yearsetID,
"model_id" : modelID
},
dataType: "html",
success : function (html) {
if (callback)callback(html);
}
});
}
function afterGetSubModels(html) {
if (html) doStuffModels();
}
function doStuffModels() {
// found a submodel
$('#submodel').html(html);
$('#submodel').on('change', subModuleChange);
}
function subModuleChange() {
var submodelID = $(this).val(),
yearsetID = $('#year').val(),
makesetID = $('#make').val(),
modelsetID = $('#model').val();
getBodyTypes(afterGetBodyTypes);
function getBodyTypes(callback) {
$.ajax({
type : 'POST',
url : 'lib/ajaxData.php',
data : {
"action" : 'getbodytypes',
"year" : yearsetID,
"make" : makesetID,
"model" : modelsetID,
"submodel": submodelID
},
success: function (html) {
if (callback)callback(html);
}
});
}
function afterGetBodyTypes(html) {
// found a bodytype
if (html) {
$('#bodytype').html(html);
$('#bodytype').on('change', function () {
//more dropdowns
});
}
}
}
因為#submodel
在所有這些操作的開始就已經存在並且沒有被替換,所以您只需將那個處理程序從嵌套中拉出即可。 它不需要在里面。 我還建議您將ajax處理切換為使用Promise,因為這將使您將來避免在復合操作上嵌套。
看來#bodytype
已經存在,也沒有被替換,因此您可以為它做同樣的事情。 只需從嵌套中拉出事件處理程序即可。
如果動態替換或加載了這些元素中的任何一個,則可以使用委托事件處理。 我將在下面顯示兩個版本的代碼。
這是一個假定#submodel
和#bodytype
不變且未替換的版本(如果替換其子html很好):
$('#model').on('change',function(){
var modelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
if (modelID) {
$.ajax({
url:'lib/ajaxData.php',
type: "POST",
data: {
"action" : 'getsubmodels',
"makeset_id" : makesetID,
"yearset_id" : yearsetID,
"model_id" : modelID
},
dataType: "html",
}).then(function(html){
if (html) {
// found a submodel
$('#submodel').html(html);
} else {
//no submodel
}
});
} else {
$('.product').html('Select Some Values');
}
});
$('#submodel').on('change',function(){
var submodelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
var modelsetID = $('#model').val();
$.ajax({
type:'POST',
url:'lib/ajaxData.php',
data: {
"action" : 'getbodytypes',
"year": yearsetID,
"make" : makesetID,
"model" : modelsetID,
"submodel" : submodelID
}
}).then(function(html){
// found a bodytype
if(html) {
$('#bodytype').html(html);
}
});
});
$('#bodytype').on('change',function(){
//more dropdowns
});
而且,如果這些元素中的另一個被動態替換,那么這里是一個使用委托事件處理的版本。 委托事件處理使用jQuery的.on()
的.on()
一種形式。 代替:
$(selector).on(event, fn);
它用:
$(staticParentSelector).on(event, dynamicElementSelector, fn);
從技術上講,這是如何將事件處理程序分配給靜態父對象(不會動態替換的對象)的。 然后,它使用事件冒泡(在子事件中發生的事件在父級層次結構中冒泡並提供給父級元素)。 當事件到達父元素時,jQuery委托的事件處理將檢查該事件是否起源於我們感興趣的子選擇器。如果是,它將觸發事件處理程序。 這適用於動態創建的元素,因為實際上沒有事件處理程序附加到您感興趣的動態創建的元素上。相反,當動態對象上發生的事件冒泡到父級時會捕獲該事件,並從那里觸發該事件處理程序。
我們不會一直使用委托事件處理,因為它有一些應注意的缺點。 這會降低CPU的效率,因為父母可能不得不檢查來自許多不同孩子的大量冒泡事件,只是為了找到它要尋找的孩子。 而且,您無法在事件的生命周期中盡早處理該事件,因此,如果您要嘗試阻止默認操作(例如阻止提交表單),那么在事件冒泡時可能為時已晚給父母 而且,並非所有事件都不會冒泡(盡管大多數事件會冒泡)。 這些問題似乎都不對您造成問題,因此,這是使用委托事件處理的代碼。
由於我不知道您的HTML,因此我只是選擇document.body
作為將事件處理程序附加到的靜態元素。 通常最好選擇不被替換的最接近的父元素,因此在您的實際HTML中,它可能是更接近的父元素。
$(document.body).on('change', '#model', function(){
var modelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
if (modelID) {
$.ajax({
url:'lib/ajaxData.php',
type: "POST",
data: {
"action" : 'getsubmodels',
"makeset_id" : makesetID,
"yearset_id" : yearsetID,
"model_id" : modelID
},
dataType: "html",
}).then(function(html){
if (html) {
// found a submodel
$('#submodel').html(html);
} else {
//no submodel
}
});
} else {
$('.product').html('Select Some Values');
}
});
$(document.body).on('change', '#submodel', function(){
var submodelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
var modelsetID = $('#model').val();
$.ajax({
type:'POST',
url:'lib/ajaxData.php',
data: {
"action" : 'getbodytypes',
"year": yearsetID,
"make" : makesetID,
"model" : modelsetID,
"submodel" : submodelID
}
}).then(function(html){
// found a bodytype
if(html) {
$('#bodytype').html(html);
}
});
});
$(document.body).on('change', '#bodytype', function(){
//more dropdowns
});
在問題的另一部分中,如果您只想在選擇了非默認值時顯示下一個下拉列表,則可以像下面這樣通用:
$(".select-boxes select").on("change", function(e) {
// if we have a value here, then reveal the next select div
if ($(this).val()) {
// get parent div, then get next div, then show it
$(this).closest(".small-12").next().show();
}
});
首選方法是使用延遲事件,而不是回調語法。 他們在jQuery 1.5中引入,它們使用的語法與ES6所承諾的非常相似。 這里有一個教程由SitePoint的話題。
簡而言之,不是傳遞回調函數,而是將.done()
.fail()
.always()
和.always()
.then()
方法鏈接到$.when
調用上。 $.ajax()
調用是$.when()
方法的參數。
這是一個例子:
$.when(
// Make an AJAX request.
$.ajax( .. )
// The when block will return the jqXHR Object
// You could also use the $.post() or $.get() methods here.
)
.done(
// any code here will only run if the .when() request is successful
)
.fail(
// Code here will only run if the .when() request fails
)
.always(
// Code placed here will always run. Regardless of success or failure.
// This is usually placed last for additional cleanup, etc. that is
// performed whether the .when() returned success or failure.
)
希望這會有所幫助!
我看到您有很多嵌套的ajax
調用。
這就是我們所說的
Callback hell
我建議您將Promise
方式與Ajax一起使用,而不要使用回調,但這也會導致Promise hell
。
因此,唯一的解決方案是使用ES2017 async / await語法; 因此,像這樣更改您的代碼。
$('#model').on('change',async function(){
var modelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
if (!modelID)
return $('.product').html('Select Some Values');
try {
var html = await $.ajax({
url:'lib/ajaxData.php',
type: "POST",
data:{
"action" : 'getsubmodels',
"makeset_id" : makesetID,
"yearset_id" : yearsetID,
"model_id" : modelID
},
dataType: "html"
}) //If the ajax fail with 404, 500, or anything error, will be catched below...
if (!html)
return console.log('No submodel');
$('#submodel').html(html);
$('#submodel').on('change', async function(){
var submodelID = $(this).val();
var yearsetID = $('#year').val();
var makesetID = $('#make').val();
var modelsetID = $('#model').val();
try {
var html2 = await $.ajax({
type:'POST',
url:'lib/ajaxData.php',
data: {
"action" : 'getbodytypes',
"year": yearsetID,
"make" : makesetID,
"model" : modelsetID,
"submodel" : submodelID
}
})
if (!html2)
return console.log('No submodel 2')
$('#bodytype').html(html);
$('#bodytype').on('change',async function(){
//more dropdowns
})
} catch (e) {
console.log('Error 2nd ajax', e)
}
})
} catch (e) {
console.log('Error in 1st ajax', e)
}
})
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.