multiple knockout for data javascript jquery knockout.js

javascript - data - Cargue la página de forma asincrónica en Knockoutjs



knockout js data bind text (2)

Quiero cargar una página de inmediato, luego cargar datos para completar los cuadros select2 después. Usando Knockout, finalmente no recibo ningún error, pero no veo elementos en mis cuadros de select select2. Cargando sincrónicamente desde el servidor funciona, pero muy lento (debido a obtener app_names ). Tengo hasta ahora:

<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>

La inspiración para ayudar a cargar la página se encontró en espera de un resultado de Ajax para unir el modelo nocaut

Quiero cargar html, voy a hacer el gif giratorio que dice "cargar", hacer una llamada AJAX para obtener mi app_names , y cuando lleguen app_names agrego a los cuadros select2 e inicializo select2.


Aquí hay un violín que funciona para enlazar datos con jquery select2 desde una llamada ajax. http://jsfiddle.net/LkqTU/33425/

no estoy seguro de si hay una mejor manera de hacerlo, pero coloco los datos en la parte de actualización del enlace personalizado en lugar del init, ya que está siendo cargado por ajax y puede que no sea el suyo al principio.

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); } };


Esto podría ayudarlo a completar sus tareas de carga de elementos en el DOM después de una llamada AJAX.

Inicialice su viewmodel (var PageModel en mi instancia) primero.

Las listas de selección son Arrastres observables . Inicialízalos vacíos, haz tu llamada ajax.

La llamada ajax se difiere y la función .then () tiene dos params, un xhr success y xhr fail.

Si ajax tiene éxito, coloque los resultados en el Array observable.

Configure algunos condicionales en su html para evitar que KO arroje errores que indiquen que no hay datos aquí, o que las propiedades a las que intenta acceder no están disponibles.

Finalmente, aproveche el hecho de que puede suscribirse a observables y hacer algo cuando el valor cambie, en este caso pasa de una matriz vacía a una matriz con datos. Cuando sepa que tiene datos, inicialice su select2 cosas como mejor le parezca.

Puede hacer todo tipo de cosas ordenadas usando llamadas asincrónicas a datos. Puede utilizar un enlace de clic para ejecutar una función que carga datos en algún otro observable para recuperar una lista de elementos o una nueva imagen (por ejemplo, el segundo ejemplo).

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>