简体   繁体   中英

Is this a correct way to use the Deferred object?

I'm building a App that need to list the categories and subcategories of a Product.

When a user selects a category, the subcategories related to this category are loaded from the server with ajax, but only if they have not been loaded later (in this case they are loaded from the DOM).

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

Because the code was getting full of callback here and there, I decided to use the jQuery Deferred object, but I do not know if this is the correct implementation. Could someone tell me I did the right thing, or should I do differently?

I don't see anything glaringly incorrect. All in all, you are using the deferred in the correct fashion: to abstract away the possibly dual-synchronistic nature of your method. Now, that being said, if this were code appearing in my codebase, this is how I would write it. The main points being: don't use for in on arrays, build strings with arrays, consistent naming and spacing, and just some other terse JS preferences. These are a matter of taste, so otherwise, good job:

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

As a side note, just wanted to bring up that you don't have any handling of problems if the ajax request fails. You would need to reject in that case, and make sure you write fail methods. Just a heads up.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM