I want to load the page right away and then load the data to fill select2 boxes. Using Knockout, I will not get errors, but I do not see any elements in my select2 select
. Downloading synchronously from the server works, but is very slow (due to receiving app_names
). I still:
<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> <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"> <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> <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="["cart", "catalog", "common-ui", "content", "ContentServices", "cyc", "deliverFromStore", "fbr", "fbt", "irg", "localization", "mylist-domain-service", "mylist-service", "mylist-ui", "nlpplus-service", "nlpservices", "orangegraph", "passbookService", "pricing", "promotion", "recommendations", "registry", "relatedsearch", "review_service", "sbotd-svcs", "SearchNavServices", "shipping", "SpecialBuy", "store-search", "storefinder", "typeahead2", "vectorsearch", "wayfinder"]"> <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=""> <meta id="my-data" data-app-names="["cart", "catalog", "common-ui", "content", "ContentServices", "cyc", "deliverFromStore", "fbr", "fbt", "irg", "localization", "mylist-domain-service", "mylist-service", "mylist-ui", "nlpplus-service", "nlpservices", "orangegraph", "passbookService", "pricing", "promotion", "recommendations", "registry", "relatedsearch", "review_service", "sbotd-svcs", "SearchNavServices", "shipping", "SpecialBuy", "store-search", "storefinder", "typeahead2", "vectorsearch", "wayfinder"]"> <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>
Inspiration to help load the page was generally found awaiting an ajax result to bind a knockout model
I want to load html, I will make a rotating gif that says βloadingβ, make an AJAX call to get my app_names
, and when the app_names
comes in, I add them to select2 and initialize select2.