我正在构建一个需要列出产品类别和子类别的应用程序。
当用户选择一个类别时,与该类别相关的子类别将从服务器加载ajax,但前提是它们以后未加载(在这种情况下,它们是从DOM加载的)。
代码:http://jsbin.com/abijad/edit#javascript,html
var $form = $('#new-product-form');
$form.on( 'change', 'select.product-categories', function( e ) { //I'm using event delegation for a future feature...
getSubCategories( $(this).val() ).done( function( $subCategoriesEl ){
$form.find( 'select.product-subcategories' ).not( $subCategoriesEl ).hide();
$subCategoriesEl.show();
});
});
var getSubCategories = function( categoryId ) {
var dfd = $.Deferred(),
$alreadyisLoaded = $form.find( 'select.product-subcategories' ).map( function( idx, el ){
if( parseInt( $( this ).data( 'category' ), 10 ) === parseInt( categoryId, 10 ) ){
return el;
}
});
if( $alreadyisLoaded.length > 0 ){ //don't request subCategories that already are loaded
console.log( 'SubCategory loaded from DOM' );
dfd.resolve( $alreadyisLoaded );
} else {
var subCategoriesHtml = '<select data-category="' + categoryId + '" class="product-subcategories">';
$.get( '/', { //The ajax request will only be simulated
categoryId : categoryId
}, function ( result ) {
//simulate success :X
result = {"status":1,"data":[{"name":"Sub-Category I","id":1},{"name":"Sub-Category II","id":2},{"name":"Sub-Category III","id":3}]};
console.log( 'SubCategory loaded with Ajax' );
if( result.status === 1 ) { //Success
for( var subCategoryInfo in result.data) {
if( result.data.hasOwnProperty( subCategoryInfo ) ){
subCategoriesHtml += '<option value="' + result.data[subCategoryInfo].id + '">';
subCategoriesHtml += result.data[subCategoryInfo].name + '</option>';
}
}
subCategoriesHtml += '</select>';
var $subCategories = $( subCategoriesHtml ).hide().appendTo( $form );
dfd.resolve( $subCategories );
} else {
dfd.reject();
}
});
}
return dfd.promise();
};
<form id="new-product-form">
<select class="product-categories">
<option value="1">Category I</option>
<option value="2">Category II</option>
<option value="3">Category III</option>
<option value="4">Category IV</option>
<option value="5">Category V</option>
</select>
<select data-category="1" class="product-subcategories">
<option value="1">SubCategory I</option>
<option value="2">SubCategory II</option>
<option value="3">SubCategory III</option>
<option value="4">SubCategory IV</option>
<option value="5">SubCategory V</option>
</select>
</form>
因为代码在这里和那里充满了回调,我决定使用jQuery Deferred对象,但我不知道这是否是正确的实现。有人可以告诉我,我做了正确的事情,还是我应该采取不同的做法?
答案 0 :(得分:1)
我没有看到任何明显错误的东西。总而言之,您正在以正确的方式使用延迟:抽象出您方法的可能双同步性质。现在,话虽这么说,如果这是代码出现在我的代码库中,这就是我写它的方式。要点是:不要在数组上使用for in
,使用数组构建字符串,一致的命名和间距,以及其他一些简洁的JS首选项。这些都是品味问题,所以不然,好工作:
(function() {
var getSubCategories = function ( categoryId ) {
categoryId = +categoryId;
return $.Deferred( function ( dfd ) {
var isLoaded = form.find( 'select.product-subcategories' )
.map( function ( index, el ) {
if ( +$( this ).data( 'category' ) === categoryId ) {
return el;
}
}),
markup = [ ];
if ( isLoaded.length ) {
console.log( 'SubCategory loaded from DOM' );
dfd.resolve( isLoaded );
} else {
markup.push( '<select data-category="' + categoryId + '" class="product-subcategories">' );
var request = $.ajax({
url: '/',
data: { categoryId: categoryId }
});
request.done( function ( result ) {
//simulate success :X
result = {"status":1,"data":[{"name":"Sub-Category I","id":1},{"name":"Sub-Category II","id":2},{"name":"Sub-Category III","id":3}]};
var status = result.status,
data = result.data,
i = 0,
il = data.length,
current;
console.log( 'SubCategory loaded with Ajax' );
if ( status !== 1 || !data ) {
dfd.reject();
return;
}
for ( current = data[ i ]; i < il; i++ ) {
markup.push( '<option value="' + current.id + '">' );
markup.push( current.name + '</option>' );
}
markup.push( '</select>' );
dfd.resolve( $( markup.join( '' ) ).hide().appendTo( form ) );
});
}
}).promise();
};
var form = $( '#new-product-form' )
.on( 'change', 'select.product-categories', function ( e ) {
getSubCategories( $( this ).val() )
.done( function( el ) {
form.find( 'select.product-subcategories' )
.not( el )
.hide()
el.show();
});
});
});
作为旁注,如果ajax请求失败,只是想提出你没有任何问题的处理。在这种情况下,您需要reject
,并确保编写fail
方法。只是一个抬头。