2017-02-15 8 views
0

Попытка создать цепочку с выделенной формой, я сделал это, когда у меня есть необходимое количество выборок с использованием запросов $ .ajax с использованием .on ('change'), но все начинается чтобы выйти из-под контроля, когда мне нужно отобразить более 4 вариантов выпадающего списка, которые могут или не могут существовать на основе предыдущих выборов. Есть ли способ реорганизовать этот код, используя обещания или что-то еще. Может быть, указать мне в правильном направлении? Вот пример фрагмента кода.Избегайте Ajax Callback Hell «Nesting»

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

EDITED показать 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> 
+4

Подробнее о [делегирование] (https://learn.jquery.com/events/event-delegation/#event-propagation). Это позволит вам присоединить обработчики событий к динамически созданным элементам, как вы это делаете. – Mikey

+0

Дополнительная информация: http://stackoverflow.com/questions/203198/event-binding-on-dynamically-created-elements –

+0

Вы можете создать одну функцию и использовать логику для установки значений для вызова по завершении '$ .ajax () ', вместо того, чтобы вставлять' $ .ajax() 'вызовы, где параметры и обработчик' success' по существу одинаковы. – guest271314

ответ

0

Поскольку #submodel уже существует в начале всего этого и не будет заменен, вы можете просто тянуть, что даже обработчик из вложенности. Он не должен быть внутри. Я также предлагаю вам переключить обработку ajax на использование обещаний, потому что это позволит вам предотвратить вложенность сложных операций в будущем.

Похоже, что #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's .on().Вместо того, чтобы:

$(selector).on(event, fn); 

он использует:

$(staticParentSelector).on(event, dynamicElementSelector, fn); 

Технически, как это работает в том, что он назначен обработчик события статический родительский объект (тот, который не динамически заменить). Затем он использует событие bubbling (в котором события, которые происходят у детей, пузырятся по родительской иерархии и предлагаются родительским элементам). Когда событие попадает в родительский элемент, обработка делегированных событий jQuery проверяет, произошло ли это событие в дочернем селекторе, который нас интересует. Если это так, он запускает обработчик событий. Это работает с динамически создаваемыми элементами, потому что никакой обработчик событий не привязан к динамически созданному элементу, который вас интересует. Вместо этого события, возникающие в этом динамическом элементе, захватываются, когда они выходят из строя родителям, а обработчик событий уволен оттуда ,

Мы не используем делегированную обработку событий все время, потому что у нее есть несколько недостатков, о которых следует знать. Это немного меньше эффективности ЦП, потому что родителям, возможно, придется проверять много пузырящихся событий от множества разных детей, чтобы найти тот, который он искал. И вы не справляетесь с этим событием на раннем этапе своего жизненного цикла, поэтому, если вы пытаетесь предотвратить действие по умолчанию (например, запретить отправку формы), может быть слишком поздно к тому моменту, когда событие пузырится вверх для родителя. И не все события не пузырятся (хотя большинство из них). Ни один из этих проблем не кажется, что это будет проблемой для вас, вот ваш код с использованием делегированной обработки событий.

Поскольку я не знаю вашего 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(); 
    } 
}); 
+0

Благодарим вас за подробный ответ. Это очень помогает. Я добавил HTML к оригинальному вопросу. Моя основная проблема заключается в том, что #bodytype отображается, если #model не возвратил #submodel и так далее. – Paully

+0

@Paully - Это совершенно другая проблема, о которой ваш вопрос вообще не упоминается. Вам нужно будет описать, какой именно сценарий вы пытаетесь решить, и в чем проблема. И stackoverflow действительно хмурится ситуациями, когда вопрос превращается во что-то совершенно иное, чем было изначально описано, особенно после того, как уже есть куча ответов. Может быть, вы должны принять эти улучшения кода и задать новый вопрос, который говорит о том, чтобы отображать '# bodytype' в некоторых обстоятельствах? – jfriend00

+0

Спасибо, я догадывался, что не был ясен, я упомянул, что больше выпадающих списков может существовать или не существовать на основе предыдущих выборов. Я думаю, что мой вопрос состоял из двух частей, рефакторинг кода, чтобы было легче читать и отображать выпадающие списки на основе предыдущих выборов. Прошу прощения, если я расплывчато. Ваши примеры по-прежнему очень полезны. – Paully

0

Предпочтительный способ заключается в использовании отложенных событий, а не синтаксис обратного вызова. Представленные в jQuery 1.5, они используют синтаксис, который очень похож на ES6. Вот tutorial по теме SitePoint.

Короче говоря, а не переходящая в функции обратного вызова вы ЦЕПЬ .done(), .fail(), .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. 
) 

Надеется, что это помогает немного!

+1

Примечание. 'JQuery.ajax()' возвращает объект обещания jQuery, '$ .when()' не требуется для одиночного вызова '$ .ajax()'. – guest271314

+0

Спасибо, я исчерпывающе изучил этот метод, но не могу понять, как это будет работать с несколькими событиями .on ('change') ... – Paully

1

Более читаемый

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

     } 
     } 
    } 
0

Я вижу, что у вас много вложенных звонков 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) 
    } 
}) 

 Смежные вопросы

  • Нет связанных вопросов^_^