Kendo KO list not initialized after valueHasMutated

I am working on a project that requires me to use Kendo UI with Knockout.js for a mobile application, since I use the knockout-kendo library to link these libraries, the application consists of a simple list of products with detailed views for each product and shopping cart, however, I am having some problems updating the number of items in my cart.

I use knockout-kendo bindings in my application as follows:

<div data-role="view" id="cart" data-title="Cart" data-layout="main-layout"> <div data-bind="if: items().length == 0">No items currently in cart</div> <ul data-role="listview" data-style="inset" data-bind="kendoListView: { data: items, template: cartTemplate }"></ul> </div> 

where is the template:

 <script type="text/x-kendo-template" id="cartListTemplate"> <div class="km-listview-link cart-item-container" data-id="#= Id #"> <div class="product-image"> <img src="#= ImageUrl #"> </div> <div class="product-description"> <p>#= Name #</p> <p>#= formattedPrice #</p> <p>#= quantity #</p> </div> <a data-role="button" data-icon="delete" class="km-primary" data-bind="click: removeItem">Delete</a> </div> </script> 

and ViewModel:

 CartViewModel : function () { var self = this; globalKo.cartItems = self.items = ko.observableArray(JSON.parse(localStorage.getItem('cart')) || []); self.cartTemplate = kendo.template($('#cartListTemplate').html()); self.removeItem = function (vm, event) { var element = $(event.target).parents('div.cart-item-container'); productId = element.data('id'); var cartItem = globalKo.cartItems().filter(function (element) { return element.Id == productId; })[0]; if (cartItem.quantity > 1) { cartItem.quantity --; } else { self.items.remove(cartItem); } app.saveCart(); self.items.valueHasMutated(); } } 

All this works as intended, except that when the value of the array mutates (the valueHasMutated function valueHasMutated called or the array has an element added or deleted), suddenly the buttons stop being buttons and turn into plain text, they also work, as they stop calling the function to which they are attached. As you can see from the code fragments, click binding is done with markup and does not work properly.

It might be worth noting that I call the valueHasMutated function, because otherwise the view does not update the number of items in the cart.

To illustrate the problem, here are a few images:

Before pressing a button

Before pressing a button

After pressing the button

After pressing the button

I donโ€™t quite understand why this is happening, I assume that this has something to do with the Kendo user interface and not so much with knockout.js.

I also made a fiddle demonstrating the problem, you can find it here

+7
kendo-ui knockout-kendo
source share
2 answers

This article states that there is no full support for integrating Kendo and Knockout ( http://www.telerik.com/blogs/knockout-js-and-kendo-ui---a-potent-duo ), so in some scenarios some work needs to be done.

First try using a knockout pattern:

 <script type="text/html" id="cartListTemplate"> <div class="km-listview-link cart-item-container" data-id="text: Id"> <div class="product-image"> <img data-bind="attr:{src: ImageUrl}"> </div> <div class="product-description"> <p data-bind="text: Name"></p> <p data-bind="text: formattedPrice"></p> <p data-bind="text: quantity"></p> </div> <button data-icon="delete" class="km-big" data-bind="kendoMobileButton: $root.removeItem">Delete</button> </div> </script> 

Note that the 'a' tag has been changed to a simple button tag that binds it to a mobile kendo button ( https://rniemeyer.imtqy.com/knockout-kendo/web/Button.html ). Then you need to get a notification about using a knockout template instead of Kendo:

 <div data-role="view" id="cart" data-title="Cart" data-layout="main-layout"> <div data-bind="if: items().length == 0">No items currently in cart</div> <ul data-role="listview" data-style="inset" data-bind="kendoListView: { data: items, template: 'cartListTemplate', useKOTemplates: true }"></ul> </div> 

Finally, for the delete function to work correctly, bind it to the view model and get the current element as a parameter:

 self.removeItem = function(item) { if (item.quantity > 1) { var cartItem = self.items().filter(function(element) { return element.Id == item.Id; })[0]; cartItem.quantity--; self.items.valueHasMutated(); } else { self.items.remove(item); } app.saveCart(); }.bind(self); 

The only thing you need to know is that the value has mutated, updates the view, which sometimes ruins the Kendo widgets, so the Kendo knockout library uses regular tags that define the role in the data binding attribute. This is because the knockout kendo library does not currently support data role initialization and instead uses data binding initialization.

Please find a working example: https://jsfiddle.net/aveze/Lykducos/

+2
source share

you can try knockout and knockout-kendo patterns

  • In your opinion

     <div data-role="view" id="cart" data-title="Cart" data-layout="main-layout"> <div data-bind="if: !items().length === 0">No items currently in cart</div> <div data-bind="if: items().length"><p data-bind="text: items().length"></p><p> items currently in the cart</p></div> <ul data-role="listview" data-style="inset" data-bind="kendoListView: { data: items, template: 'cartListTemplate', useKOTemplates: true }"></ul> </div> 

  • create a named template

     <script type="text/html" id="cartListTemplate"> <li class="km-listview-link cart-item-container" data-bind="attr: {'id': Id}"> <div class="product-image"> <img data-bind="attr: {'src': ImageUrl}">> </div> <div class="product-description"> <p data-bind="text: Name"></p> <p data-bind="text: formattedPrice"></p> <p data-bind="text: quantity"></p> </div> <a data-role="button" data-icon="delete" class="km-primary" data-bind="click: $root.removeItem">Delete</a> </li> 

  • remove elements by splicing an array

     this.removeItem = function (item) { const deleteIndex = this.items.findIndex(i => i.Id === item.Id) if (deleteIndex > -1) { this.items.splice(deleteIndex, 1) } }.bind(this) 
0
source share

All Articles