2017-01-24 14 views
6

Tôi muốn tải trang ngay lập tức, sau đó tải dữ liệu để điền vào các hộp select2 sau đó. Bằng cách sử dụng Knockout, cuối cùng tôi không nhận được lỗi nào, nhưng không thấy mục nào trong các hộp select2 select của tôi. Đang tải đồng bộ từ các công trình máy chủ, nhưng rất chậm (vì nhận được app_names). Tôi có cho đến nay:Tải trang không đồng bộ trong 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> 

Cảm hứng để giúp tải trang ở tất cả đã được tìm thấy trong wait for ajax result to bind knockout model

Tôi muốn tải html, tôi sẽ làm gif quay nói rằng "tải", làm cho một Gọi AJAX để nhận số app_names của tôi và khi app_names đến, tôi thêm chúng vào các hộp select2 và khởi tạo select2.

Trả lời

2

đây là một fiddle làm việc cho databinding các jquery Select2 từ một cuộc gọi ajax. http://jsfiddle.net/LkqTU/33425/

không chắc chắn nếu có cách nào tốt hơn để làm điều đó nhưng tôi đặt dữ liệu trong phần cập nhật của ràng buộc tùy chỉnh thay vì init vì nó đang được ajax nạp và có thể không phải là đầu tiên của chúng.

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

Là một thay thế, bạn chỉ có thể quấn điều khiển 'select2' bên trong một điều kiện' ko if: createSelect2', trong đó 'createSelect2' được kỳ vọng là một' boolean trả về 'có thể quan sát được '. Mặc định nó thành 'false' và một khi' lời hứa' trả về từ cuộc gọi 'ajax', đặt nó thành' true'. Điều này sẽ xây dựng sự kiểm soát sau khi bạn chắc chắn rằng bạn có dữ liệu cần thiết và vẫn có thể điều khiển 'init' như được thực hiện bình thường .. – gkb

+0

@gkb Tôi nghĩ rằng tôi đã thử một cái gì đó tương tự, gói select2 trong một" .done() " Điều.idk những gì tôi đã làm sai mặc dù, tôi chắc chắn tôi chỉ thực hiện nó sai như tôi tìm hiểu các js công cụ không đồng bộ – codyc4321

+0

này là rất đẹp và dường như làm việc cho bất kỳ phần tử select2 vì vậy tôi chọn nó – codyc4321

2

Điều này có thể giúp bạn quấn đầu xung quanh tải các mục vào DOM sau cuộc gọi AJAX.

Khởi tạo chế độ xem của bạn (var PageModel trong trường hợp của tôi) trước tiên.

Danh sách lựa chọn là observableArrays. Khởi tạo chúng trống, thực hiện cuộc gọi ajax của bạn.

Cuộc gọi ajax được hoãn lại và hàm .then() có hai tham số, thành công xhr và xhr không thành công.

Nếu ajax đặt thành công kết quả vào ObservableArray.

Thiết lập một số điều kiện trong html của bạn để ngăn KO khỏi lỗi khi nói rằng không có dữ liệu ở đây hoặc thuộc tính bạn đang cố gắng truy cập không khả dụng.

Cuối cùng hãy chú ý đến việc bạn có thể đăng ký các quan sát và thực hiện điều gì đó khi giá trị thay đổi, trong trường hợp này nó đi từ một mảng trống sang mảng có dữ liệu. Khi bạn biết nó có dữ liệu, hãy khởi tạo công cụ select2 của bạn, tuy nhiên bạn thấy phù hợp.

Bạn có thể làm đủ mọi thứ gọn gàng bằng cách sử dụng các cuộc gọi không đồng bộ đến dữ liệu. Bạn có thể sử dụng một liên kết nhấp chuột để chạy một hàm tải dữ liệu vào một số khác có thể quan sát để truy xuất danh sách các mục hoặc một hình ảnh mới (ví dụ thứ hai của IE).

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 có vẻ khá thú vị. ví dụ tuyệt vời. cả hai đều đúng, idk người để chọn – codyc4321

Các vấn đề liên quan