0

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)

antonio_mg
  • 449
  • 1
  • 5
  • 21
  • instead of removing all, can you please try to reset, like self.items({}). it reset the array. then add your new items – Well Wisher Apr 07 '16 at 05:27
  • Thank you man, I test this solution but still unsolved. What I'm trying to accomplish is that when remove items from observableArray and length equals 0, row with message telling there's no coincidences is displayed. – antonio_mg Apr 07 '16 at 05:36
  • can you please create a fiddle for the same..? – Well Wisher Apr 07 '16 at 05:37
  • I have no idea how to create one, I'm very new with programmation, I will investigate how to create one right now, thank you for your help, please be patient. – antonio_mg Apr 07 '16 at 05:40
  • hold on.. am doing one from here – Well Wisher Apr 07 '16 at 05:42
  • OP, see [mcve] for guidance on creating a repro. – Jeroen Apr 07 '16 at 06:04
  • 1
    Also, note that you'll want to use [custom bootstrap modal binding handlers](http://stackoverflow.com/a/22707080/419956) when combining them with KnockoutJS. Don't do DOM manipulation (e.g. `on` handlers) inside the *view model*, that's what binding handlers are for. – Jeroen Apr 07 '16 at 06:05

3 Answers3

1

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.

Community
  • 1
  • 1
Jeroen
  • 60,696
  • 40
  • 206
  • 339
  • When modal loads the first time, observableArray is empty and condition items().length == 0 works fine, but when I search products, and the observableArray is populated, I close modal and call removeAll, this clear the array and items().length is equals zero but when I open modal again, the condition items().length == 0 is not working because this html is not being displayed There's no coincidences – antonio_mg Apr 07 '16 at 06:22
  • Could be, but *the code in the question is not sufficient to reproduce that*. – Jeroen Apr 07 '16 at 08:06
0

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 emptyself.array

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

http://jsfiddle.net/LCQQH/

Thank you

Well Wisher
  • 1,825
  • 1
  • 15
  • 20
  • Don't think this correct advice. [The relevant docs](http://knockoutjs.com/documentation/observableArrays.html#remove-and-removeall) state that `removeAll` is a proper method on `observableArray`s and should update the view correctly. Introducing custom array cleaning functions wouldn't change much. – Jeroen Apr 07 '16 at 06:03
  • I don't know whats going on, for test, I print the value of items().length and is being updated properly, I meant, when call removeAll, length is equals to zero but for some reason this sentence is not working. Thanks bot for your help, I'm gonna try to find out whats wrong here. – antonio_mg Apr 07 '16 at 06:14
  • @Well whiser, I have change removeAll for self.items([]), but still not working, thank you for your help and for create that jsfiddle, I really appreciated. – antonio_mg Apr 07 '16 at 06:17
0

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.

antonio_mg
  • 449
  • 1
  • 5
  • 21