Wednesday, June 27, 2012

IE8 doesn't like to play nice with knockout JS templates

I've come across a bizarre issue with IE8 and knockout JS templates. In a project I'm working on, there is a UL list bound against knockoutJS with a foreach binding:

 <ul data-bind="foreach: choices">
<!-- ko template: {name: $data.template, data: $data} -->
<!-- /ko -->
</ul> 
Inside the template, each list item has a delete button:
<button data-bind="click: $parents[1].deleteMe">x</button>

(Noah Sussman)
This delete button works great. The view model has a deleteMe method on it, that takes the currentItem parram passed in by knockout (see http://knockoutjs.com/documentation/click-binding.html ), and calls choices.remove(currentItem). Query viewModel.choices().length in any browser (even IE 8), and it will correctly return the newer, lesser number of elements.

Unfortunately, it doesn't actually remove the item from the dom in IE8. The template will still show the deleted element, even though the deleted element is removed from the viewmodel.

The only solution I have found so far is, unfortunately, to remove the template tag and stick everything inside the UL. Unfortunately, when using multiple templates inside a loop, this can make for some messy code.

Here's a jsfiddle to test with. In IE8, when deleting an element, notice how the bound .length will decrease, while the elements remain in place. In other browsers, the LI is (correctly) removed.

http://jsfiddle.net/wx78s/

Anyone know of a better way to fix this?

Update:

While this does not solve the problem of templates-inside-a-for-loop, if all you need is a single template, there's an easy workaround.

KnockoutJS templates can include a "foreach" param inside the template itself. This works in IE8, and allows you to iterate over templates in IE8.

2 comments :

  1. I just had a similar problem (maybe?) where ie8 wasn't rendering my virtual bindings e.g.

    <!-- ko template:{name:'account-template',with: Accounts } -->
    <!-- /ko -->

    My solution was to put '' around the 'with' parameter. Also needed to do this with a 'for' parameter on a label element.

    ReplyDelete