简体   繁体   中英

Knockout apply binding after observable array change

I have a table with items obtained from a knockout observable array, the user enter a search text and coincidences populate the observableArray, this table is shown on a modal. Each item has a button to open another modal with some functionality (ommited because is not relevant). The table must display the items if observableArray length is greater than 0, otherwise, must display one row to indicate there's no results to display.

<tr style="display: none" data-bind="visible: items().length == 0">
    <td class="text-center alert alert-warning" colspan="4"><b>There's no coincidences</b></td>
</tr>

My view model:

var viewModel = function () {
    self.items= ko.observableArray([]);

    //Modal is already on html, but not visible, to show it I use this
    $('#searchProduct').modal('show');

    //When modal is closed, the table is cleaned, so the items in observableArray are removed
    $('#searchProduct').on('hidden.bs.modal', function () {
        self.items.removeAll();
    });
}

The problem is that the first time, the visible binding works fine, but when the observableArray length changes (when call removeAll on hidden), the bind is not applied again. I know is because the binding is already apply, so when the observableArray changes, the length is updated but the condition cannot render html again.

How can this be solved with knockout?

(I tried to be very specific, but if more information is needed, I can update the information to be clearer)

The code you've posted should work fine. Here's an example:

 function Item() { self.txt = ko.observable("Test observable"); } function RootViewModel() { var self = this; self.items = ko.observableArray([new Item(), new Item()]); $('#searchProduct').modal('show'); $('#searchProduct').on('hidden.bs.modal', function () { self.items.removeAll(); }); } ko.applyBindings(new RootViewModel()); 
 pre { background: white; padding: 10px; color: #333; font: 11px consolas; border: 1px solid #ddd; } 
 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/js/bootstrap.min.js"></script> <link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.6/css/bootstrap.min.css" rel="stylesheet"/> <script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.4.0/knockout-min.js"></script> <div id="searchProduct" class="modal fade"> <div class="modal-dialog"> <div class="modal-content">Fake Modal</div> </div> </div> <table> <tbody> <tr style="display: none" data-bind="visible: items().length == 0"> <td class="text-center alert alert-warning" colspan="4"><b>There's no coincidences</b> </td> </tr> <!-- ko foreach: items --> <tr> <td data-bind="text: txt"></td> </tr> <!-- /ko --> </tbody> </table> <hr>Debug info: <pre data-bind="text: ko.toJSON($root, null, 2)"></pre> 

Note however that I recommend using a custom binding handler for show/hide of a bs modal , you should not handle DOM interaction (like on handlers) inside the view models.

Calling removeAll empties the underlying array and your two observables appear to have a reference to the same array.rather than calling removeAll please do as self.items([]);

i have created a fiddle Example for adding to array and removing all from array.

http://jsfiddle.net/d7mpc6wa/4/

i cant see your complete HTML code so i created a similar example here.

HTML

<table>
    <tbody data-bind="foreach: items">
        <tr><td data-bind="text:$data.name"></td></tr>
    </tbody>
    <tbody>
        <tr data-bind="visible: items().length == 0">
            <td class="text-center alert alert-warning" colspan="4"><b>There's no coincidences</b></td>
        </tr>
    </tbody>
</table>

<button data-bind="click:cleanArray">Clean Array</button>
<button data-bind="click:addArray">Add Array</button>

ViewModel

   function VM() {
     var self = this;
     var arr = [{name:'name 1'},{name:'name 2'},{name:'name 3'}];     
     self.items = ko.observableArray(arr);
     self.cleanArray = function(){
       self.items([]);
     }

     self.addArray = function(){
       self.items([]);
       self.items(arr);
       console.log(self.items());
     };
   }
  ko.applyBindings(new VM());  

Please let me know if that helps

UPDATE

so your here we can remove items in the array as self.items([]); or self.items.removeAll(); so there is a slight difference.

self.items([]); will replace the current array with new empty array. but self.items.removeAll() will remove all the items from self.items + it will empty the array instance.

self.array = ['1', '2', '3'];
self.myArray1 = ko.observableArray(self.array);
self.myArray1.removeAll(); 

will empty the self.myArray1 + it will empty self.array

the following example clearly explained the difference, please have a look.

http://jsfiddle.net/LCQQH/

Thank you

I'm so ashamed, everything with this code I posted is fine, the error was that when I was closing modal, I was removing all the alerts class in html, which obviously was removing alert-warning in td element, so instead or remove all alert, what is was not the purpouse, I'm removing alert-danger from modal. I really sorry for posted this silly question on SO, and @Jeroen, I know that something was wrong with mixing jQuery with knockout to hide modal, only that I don't know how to do it the right way. Thank you so much for your answers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM