2017-01-24 54 views
6

我想立即加载页面,然后加载数据以填充select2框。使用淘汰赛,我终于没有得到任何错误,但在我的select2 select框中看不到任何项目。从服务器工作同步加载,但非常慢(因为得到app_names)。我至今:在Knockoutjs中异步加载页面

<head> 

    <meta charset="utf-8"> 
    <meta http-equiv="X-UA-Compatible" content="IE=edge"> 
    <meta name="viewport" content="width=device-width, initial-scale=1"> 
    <title>Admin suite</title> 

    <!-- Load javascript libraries --> 
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js"></script> 
    <script src="//cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.1/js/bootstrap.min.js"></script> 
    <script src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script> 

    <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.4/css/bootstrap.css" rel="stylesheet"> 

    <!-- best interactive input box --> 
    <link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet" /> 

    <script type="text/javascript" src="//cdn.jsdelivr.net/momentjs/latest/moment.min.js"></script> 

    <script type="text/javascript" src="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.js"></script> 
    <link rel="stylesheet" type="text/css" href="//cdn.jsdelivr.net/bootstrap.daterangepicker/2/daterangepicker.css" /> 

    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js"></script> 
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.1/knockout-min.js"></script> 

    <link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" rel="stylesheet"> 
    <script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script> 
    <!-- semantic --> 
    <!-- <link href="https://cdnjs.com/libraries/semantic-ui" rel="stylesheet"/> --> 

    <style> 
     .center { 
      float: none; 
      margin-left: auto; 
      margin-right: auto; 
     } 
     #centered { 
      width: 50%; 
      margin: 0 auto; 
      margin-top: 100 
     } 
     #middleman-datepicker { 
      cursor: pointer; 
     } 
     .column { float: left; padding: 5px 10px; } 
     .row { overflow: hidden; } 
     label { 
      display: -webkit-box; 
      display: -webkit-flex; 
      display: -ms-flexbox; 
      display: flex; 
      -webkit-box-align: center; 
      -webkit-align-items: center; 
      -ms-flex-align: center; 
      align-items: center; 
     } 
     input[type=radio], 
     input[type=checkbox] { 
      -webkit-box-flex: 0; 
      -webkit-flex: none; 
      -ms-flex: none; 
      flex: none; 
      margin-right: 10px; 
     } 
     .btn-primary, 
     .btn-primary:active, 
     .btn-primary:visited, 
     .btn-primary:focus { 
      background-color: #f49e42; 
      border-color: #8064A2; 
     } 
     .btn-primary:focus { 
      background-color: #f49542; 
     } 
     .btn-primary:hover { 
      background-color: #f48c42; 
     } 
    </style> 

    <meta id="my-data" 
     data-app-names="[&#34;cart&#34;, &#34;catalog&#34;, &#34;common-ui&#34;, &#34;content&#34;, &#34;ContentServices&#34;, &#34;cyc&#34;, &#34;deliverFromStore&#34;, &#34;fbr&#34;, &#34;fbt&#34;, &#34;irg&#34;, &#34;localization&#34;, &#34;mylist-domain-service&#34;, &#34;mylist-service&#34;, &#34;mylist-ui&#34;, &#34;nlpplus-service&#34;, &#34;nlpservices&#34;, &#34;orangegraph&#34;, &#34;passbookService&#34;, &#34;pricing&#34;, &#34;promotion&#34;, &#34;recommendations&#34;, &#34;registry&#34;, &#34;relatedsearch&#34;, &#34;review_service&#34;, &#34;sbotd-svcs&#34;, &#34;SearchNavServices&#34;, &#34;shipping&#34;, &#34;SpecialBuy&#34;, &#34;store-search&#34;, &#34;storefinder&#34;, &#34;typeahead2&#34;, &#34;vectorsearch&#34;, &#34;wayfinder&#34;]"> 

    <style> 
     .deactivate-services-box, 
     .delete-services-box { 
      width: 400px; 

     } 

     .clear-button { 
      margin-left: 10px; 
     } 

    </style> 

</head> 

<body> 
    <nav class="navbar navbar-default navbar-fixed-top"> 
     <div class="container"> 
      <div class="navbar-header"> 
       <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#bs-example-navbar-collapse-1"> 
        <span class="sr-only">Toggle Navigation</span> 
        <span class="icon-bar"></span> 
        <span class="icon-bar"></span> 
        <span class="icon-bar"></span> 
       </button> 
       <a class="navbar-brand" href="/">SLO admin suite</a> 
      </div> 

      <a data-toggle="dropdown" class="dropdown-toggle" href="#"> 
      <div id="navbar" class="navbar-collapse collapse"> 
       <ul class="nav navbar-nav navbar-right"> 
         <li class="dropdown"> 
          Navigate 
          <span class="caret"></span> 
         <ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> 
          <li class="dropdown-header"><a href="/">Middleman backfill</a></li> 
          <li class="dropdown-header"><a href="/healthchecks">Healthcheck statuses</a></li> 
          <li class="dropdown-header"><a href="/delete-services">Delete/deactivate services</a></li> 
         </ul></li> 

       </ul> 
      </div></a> 
     </div> 
    </nav> 

    <script type="text/javascript"> 
    </script> 

    <div style="padding-top: 90px; float: left;" class="container"> 
     <div > 
      <div class=""> 

<!-- https://select2.github.io/examples.html --> 


    <meta id="my-data" 
     data-app-names="[&#34;cart&#34;, &#34;catalog&#34;, &#34;common-ui&#34;, &#34;content&#34;, &#34;ContentServices&#34;, &#34;cyc&#34;, &#34;deliverFromStore&#34;, &#34;fbr&#34;, &#34;fbt&#34;, &#34;irg&#34;, &#34;localization&#34;, &#34;mylist-domain-service&#34;, &#34;mylist-service&#34;, &#34;mylist-ui&#34;, &#34;nlpplus-service&#34;, &#34;nlpservices&#34;, &#34;orangegraph&#34;, &#34;passbookService&#34;, &#34;pricing&#34;, &#34;promotion&#34;, &#34;recommendations&#34;, &#34;registry&#34;, &#34;relatedsearch&#34;, &#34;review_service&#34;, &#34;sbotd-svcs&#34;, &#34;SearchNavServices&#34;, &#34;shipping&#34;, &#34;SpecialBuy&#34;, &#34;store-search&#34;, &#34;storefinder&#34;, &#34;typeahead2&#34;, &#34;vectorsearch&#34;, &#34;wayfinder&#34;]"> 

    <style> 
     .deactivate-services-box, 
     .delete-services-box { 
      width: 400px; 

     } 

     .clear-button { 
      margin-left: 10px; 
     } 

    </style> 


    <body> 
     <div id="centered"> 
      <span data-bind="visible: currently_running_ajax"> 
       <h4>Pretending to run deactivation/deletion for 3 secs...</h4> 
       <p><img src="/static/img/loader.gif"/></p> 

      </span> 

      <div id="ajax-return-error-message" style="position:fixed; top:10%; right:45%; color: red; z-index: 999; display: none;"></div> 

      <h2>Deactivate services</h2> 
      <select class="deactivate-services-box" multiple="multiple" data-bind="foreach: app_names"> 
       <option data-bind="value: $data, text: $data"></option> 
      </select> 
      <button id="deactivate-clear-all-button" class="clear-button">Clear all</button> 

      <h2>Permanently delete services</h2> 
      <select class="delete-services-box" multiple="multiple" data-bind="foreach: app_names"> 
       <option data-bind="value: $data, text: $data"></option> 
      </select> 
      <button id="delete-clear-all-button" class="clear-button">Clear all</button> 

      <br><br> 
      <p id="empty-set-error-message" style="color: red; display: none;">Please make a selection</p> 
      <button id="submit-button" data-bind="click: submit_deactivation_and_or_deletion" class="btn-primary btn-lg" style="margin-left: 20px; ">Submit</button> 
      <button id="submit-button" data-bind="click: submit_fails_demo" class="btn-info btn-lg" style="margin-left: 20px; ">Submit will fail</button> 

     </div> 

     <script type="text/javascript"> 
      var app_names = []; 
      console.log("app names 1"); 
      // knockout 
      function DeleteServicesViewModel(){ 
       var self = this; 

       self.app_names = app_names; 
       console.log("app names 2"); 
       self.currently_running_ajax = ko.observable(false); 

       // var djangoData = $('#my-data').data(); 
       // self.app_names = djangoData.appNames; 

       self.find_any_duplicates = function(list_one, list_two){ 
        var duplicates = []; 
        for (i = 0; i < list_one.length; i++){ 
         var item = list_one[i]; 
         if (_.contains(list_two, item)){ 
          duplicates.push(item); 
         } 
        } 
        return duplicates; 
       } 

       self.display_error_message = function(error){ 
        setTimeout(
         function() { 
          $("#ajax-return-error-message").text(error) 
          $("#ajax-return-error-message").slideDown(500, function(){ 
           setTimeout(function(){ 
            $("#ajax-return-error-message").slideUp(500); 
           }, 2600); 
          }); 
         }, 300 
        ); 
       } 

       self.submit_deactivation_and_or_deletion = function(){ 
        var deactivate_values = $deactivate_services_box.val(); 
        var deletion_values = $delete_services_box.val(); 
        // alert(deactivate_values); 

        if (deactivate_values.length == 0 && deletion_values.length == 0){ 
         $("#empty-set-error-message").slideDown(500, function(){ 
          setTimeout(function(){ 
           $("#empty-set-error-message").slideUp(500); 
          }, 1700); 
         }); 

         return; 
        } 

        var duplicates = self.find_any_duplicates(deactivate_values, deletion_values); 
        if (duplicates.length){ 
         alert("We cannot both delete and deactivate the same item. You have the following duplicates:\n\n%dups%\n\nPlease remove duplicates".replace("%dups%", duplicates)); 
         return; 
        } 
        console.log('duplicates: ', duplicates); 

        self.currently_running_ajax(true); 
        $.ajax({ 
         url: "/run-deactivation-and-deletion", 
         method: "POST", 
         headers: { 
          "Content-Type": "application/json" 
         }, 
         data: ko.toJSON(
          { deactivate_list: deactivate_values, deletion_list: deletion_values } 
         ), 
         success: function(data) { 
          console.log("worked"); 
          $deactivate_services_box.val(null).trigger("change"); 
          $delete_services_box.val(null).trigger("change"); 
         }, 
         error: function(xhr, textStatus, error) { 
          console.log("failed"); 
          console.log(error); 
          self.currently_running_ajax(false); 
          self.display_error_message(error); 

         }, 
         complete: function(){ 
          self.currently_running_ajax(false); 
         } 
        }); 
       } 

       // TODO: delete after demo 
       self.submit_fails_demo = function(){ 
        var deactivate_values = $deactivate_services_box.val(); 
        var deletion_values = $delete_services_box.val(); 
        // alert(deactivate_values); 

        if (deactivate_values.length == 0 && deletion_values.length == 0){ 
         $("#empty-set-error-message").slideDown(500, function(){ 
          setTimeout(function(){ 
           $("#empty-set-error-message").slideUp(500); 
          }, 1700); 
         }); 

         return; 
        } 

        var duplicates = self.find_any_duplicates(deactivate_values, deletion_values); 
        if (duplicates.length){ 
         alert("We cannot both delete and deactivate the same item. You have the following duplicates:\n\n%dups%\n\nPlease remove duplicates".replace("%dups%", duplicates)); 
         return; 
        } 
        console.log('duplicates: ', duplicates); 

        self.currently_running_ajax(true); 
        $.ajax({ 
         url: "/run-deactivation-and-deletion-fails-demo", 
         method: "POST", 
         headers: { 
          "Content-Type": "application/json" 
         }, 
         data: ko.toJSON(
          { deactivate_list: deactivate_values, deletion_list: deletion_values } 
         ), 
         // designed to fail for demo 
         error: function(xhr, textStatus, error) { 
          console.log("failed"); 
          console.log(error); 
          self.currently_running_ajax(false); 
          self.display_error_message(error); 
         }, 
         complete: function(){ 
          self.currently_running_ajax(false); 
         } 
        }); 
       } 
      } 

      $.getJSON("/app-names", function(data){ 
       var app_names_json_string = data.app_names; 
       var app_names = JSON.parse(data.app_names); 
       console.log("app names 3"); 

       ko.applyBindings(new DeleteServicesViewModel(app_names)); 

       var $deactivate_services_box = $(".deactivate-services-box"); 
       var $delete_services_box = $(".delete-services-box"); 

       $deactivate_services_box.select2(); 
       $delete_services_box.select2(); 

       $("#deactivate-clear-all-button").on("click", function() { $deactivate_services_box.val(null).trigger("change"); }); 
       $("#delete-clear-all-button").on("click", function() { $delete_services_box.val(null).trigger("change"); }); 
      }); 

      // ko.applyBindings(new DeleteServicesViewModel()); 

     </script> 
    </body> 


</div><br> 

     </div> 
    </div> 
</body> 
</html> 

的灵感,帮助在所有的页面加载在wait for ajax result to bind knockout model

我想加载HTML被发现,我会做纺纱GIF,上面写着“装”,使一个AJAX调用来获得我的app_names,当app_names到达时,我将它们添加到select2框并初始化select2。

回答

2

这里是从一个Ajax调用数据绑定jQuery的选择2工作提琴。 http://jsfiddle.net/LkqTU/33425/

不确定是否有更好的方法来做到这一点,但我把数据放在自定义绑定的更新部分而不是init,因为它是由ajax加载的,并且可能不是它们的第一步。

ko.bindingHandlers.select2 = { 
    init: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
     ko.bindingHandlers.value.init(element,valueAccessor, allBindings); 
      $(element).select2({ 
      }) 
    }, 
    update: function(element, valueAccessor, allBindings, viewModel, bindingContext) { 
     var data = allBindings.get('select2Data'); 
     var dataUnwrapped = ko.toJS(data); 
      $(element).select2({ 
       data: dataUnwrapped 
      }) 
     var value = valueAccessor(); 
     ko.bindingHandlers.value.update(element,valueAccessor); 
    } 
}; 
+1

作为替代,你可以在'ko if:createSelect2'条件中包装'select2'控件,其中'createSelect2'应该是一个'observable'返回'布尔值'。默认为'false',一旦''promise'从'ajax'调用返回,将其设置为'true'。这将建立控制后,你确信你有必要的数据,仍然能够'初始化'控制正常完成.. – gkb

+0

@gkb我以为我尝试了类似的东西,包装select2在“.done()”事情。idk我做错了,但我确信我刚刚实现它错了,因为我学习异步js的东西 – codyc4321

+0

这是非常美丽的,似乎适用于任何select2元素,所以我选择它 – codyc4321

2

这可能会帮助您在AJAX调用之后将项目加载到DOM中的过程中。

首先初始化您的viewmodel(在我的例子中是var PageModel)。

选择列表是observableArrays。将它们初始化为空,执行您的ajax调用。

ajax调用被延迟,.then()函数有两个参数,xhr成功和xhr失败。

如果ajax成功,将结果放入observableArray。

在您的html中设置一些条件以防止KO抛出错误,指出这里没有数据,或者您尝试访问的属性不可用。

最后捎带了一个事实,即您可以订阅observables并在值发生更改时执行某些操作,在这种情况下,它将从空数组转换为包含数据的数组。当你知道它有数据时,尽管你认为合适,然后初始化你的select2东西。

你可以使用异步调用数据来做各种各样的整齐的东西。您可以使用点击绑定来运行一个函数,该函数将数据加载到其他可观察对象中以检索项目列表或新图像(如第二个示例)。

var PageModel = function(r) { 
 
    var self = this; 
 

 
    this.Select1 = ko.observableArray([]); 
 
    self.Select1.subscribe(function (val) { 
 
    if (val) { 
 
     // Run function to initialize Select2 box 
 
     // $('#some-select2-thingy').select2 stuff or whatever 
 
     console.log('This select has data now'); 
 
    } 
 
    }); 
 
    this.Loading1 = ko.observable(false); 
 
    this.Errors = ko.observableArray([]); 
 
    ajaxCall('https://api.punkapi.com/v2/beers', 'GET', self.Select1, self.Loading1, self.Errors); 
 

 
    this.Image = ko.observable(); 
 
    this.Loading2 = ko.observable(false); 
 
    this.LoadImage = function() { 
 
    ajaxCall('https://api.giphy.com/v1/gifs/random?api_key=dc6zaTOxFJmzC&tag=american+psycho', 'GET', self.Image, self.Loading2, self.Errors) 
 
    } 
 
    this.ClearImage = function() { 
 
    self.Image(null); 
 
    } 
 
}; 
 

 
window.model = new PageModel(); 
 
ko.applyBindings(model); 
 

 
function ajaxCall(url, method, selectObj, loadingObj, errorObj) { 
 
    return $.when($.ajax({ 
 
    url: url, 
 
    method: method, 
 
    beforeSend: function() { 
 
     loadingObj(true); 
 
    } 
 
    })).then(function(response) { 
 
    selectObj(response); 
 
    loadingObj(false); 
 
    }, function(error) { 
 
    errorObj.push(error); 
 
    }); 
 
};
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"/> 
 
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> 
 
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script> 
 

 
<div class="container-fluid" style="margin-top: 30px;"> 
 
    <div class="row" style="margin-bottom: 30px;"> 
 
    <div class="col-sm-6 col-sm-push-3"> 
 
     <p class="alert alert-info" data-bind="visible: Loading1()">Loading...</p> 
 
     <!-- ko if: Select1().length && !Loading1() --> 
 
     <select id="some-select2-thingy" class="form-control" data-bind="options: Select1, optionsText: 'name', optionsValue: 'id', optionsCaption: '- Select a Beer -'"></select> 
 
     <!-- /ko --> 
 
    </div> 
 
    </div> 
 
    <div class="row"> 
 
    <div class="col-sm-6 col-sm-push-3"> 
 
     <div class="text-center" style="margin-bottom: 10px;"> 
 
     <button class="btn btn-info" data-bind="click: LoadImage, text: Image() ? 'Get A Different Image' : 'Get An Image'">Get An Image</button> 
 
     <button class="btn btn-danger" data-bind="click: ClearImage, visible: Image()">Clear Image</button> 
 
     </div> 
 
     <p class="alert alert-info" data-bind="visible: Loading2()">Loading...</p> 
 
     <!-- ko if: Image() && !Loading2() --> 
 
     <!-- ko with: Image --> 
 
     <div class="text-center"> 
 
     <img data-bind="attr: {'src': data.fixed_height_downsampled_url}"> 
 
     </div> 
 
     <!-- /ko --> 
 
     <!-- /ko --> 
 
    </div> 
 
    </div> 
 
</div>

+0

brewdog看起来很酷。很好的例子。你是对的,idk谁选 – codyc4321