Load the page asynchronously in Knockoutjs

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> <!-- 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.imtqy.com/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> 

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.

+6
source share
2 answers

a script works here to bind jquery select2 to ajax call. http://jsfiddle.net/LkqTU/33425/

Not sure if there is a better way to do this, but I put the data in the custom binding update part instead of init, since it loads ajax and may not be the first.

 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); } }; 
+2
source

This can help you wrap your head around loading items in the DOM after calling AJAX.

Initialize your view model first (var PageModel in my instance).

In selection lists, observed arrays . Initialize them empty, make your ajax call.

The ajax call is deferred, and the .then () function has two parameters, success xhr and xhr do not work.

If ajax successfully puts the results into an observable array.

Set some conventions in your html so that KO doesn't throw errors saying that there is no data here or the properties you are trying to get are not available.

Finally, back to the fact that you can subscribe to observables and do something when the value changes, in this case it goes from an empty array to an array with data. When you know that it has data, initialize your select2 stuff as you see fit.

You can do all kinds of neat things using asynchronous data calls. You can use click binding to launch a function that loads data into other observables to get a list of elements or a new image (IE is the second example).

 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> 
+2
source

Source: https://habr.com/ru/post/1014391/


All Articles