Search code examples
javascriptjqueryinternet-explorerknockout.jsknockout-validation

Knockout component given incorrect binding context in Internet Explorer 11


I have a bug that I have tested in Internet Explorer and Chrome and this only happens in Internet Explorer.

I am using Knockout-3.3.0 and Knockout Validation.

I have a knockout component that uses a custom element similar to the below:

<div>

    ...

    <div data-bind="with: editableUser">
        <div data-bind="visible: !$parent.editMode">
            <user-picker params="value: $parent.user, multiple:false, hideSelection:true"></user-picker>
        </div>
    </div>

    ...

</div>

The custom element is a descendant of other nodes which are part of my view model as shown above.

The scripts dependencies and view model bindings for the page are being applied as shown below:

<script src="/Scripts/knockout-3.3.0.debug.js"></script>
<script src="/Scripts/knockout.validation.js"></script>
<script src="/Scripts/CustomBindings.js"></script>

<script>
    $(function() {
        ko.validation.init({
            insertMessages: false,
            decorateInputElement: false,
            errorElementClass: "has-error has-feedback",
            decorateElementOnModified: true
        });
    });
</script>

<script src="/Scripts/UserPicker.js"></script>
<script src="/Scripts/UserModel.js"></script>
<script src="/Scripts/CustomComponents.js"></script>
<script type="text/javascript">
    var userModel;
    $(function() {
        var userRepository = new MyModule.UserRepository();
        userModel = new MyModule.UserModel(userRepository);
        ko.applyBindings(userModel);
    }); 
</script>

The template for the component is rendered in an MVC section before the scripts; as shown below:

<template id="user-picker-template">
    <div class="user-picker"> ... </div>
</template>

The component is registered in CustomComponents.js as shown.

ko.components.register('user-picker', {
    template: { element: 'user-picker-template' },
    viewModel: MyModule.UserPicker
});

I also have custom knockout bindings in the CustomBindings.js file.

When debugging Knockout the error occurs in this function applyBindingsToNodeInternal on this line:

var initResult = handlerInitFn(node, getValueAccessor(bindingKey), allBindings, bindingContext['$data'], bindingContext);

A javascript error is thrown during the ko.applyBindings call.

The error is that the property in the first binding inside the user-picker-template is undefined, this is because the bindingContext being used is incorrect, the userModel is being passed instead of the UserPicker.

Due to the error the remaining non component related bindings are completed and then the user-picker binding is completed correctly afterwards, but because of the javascript error, some callback code on my click events don't work.

When debugging this in Chrome the binding for the user picker is ignored until after the userModel has been bound.

Does anyone know what might be causing this error?


Solution

  • It's because the template is being bound outside of the the component, directly within the <template> element. Knockout doesn't specifically skip <template> elements, but browsers that support <template> report the contents differently from a normal element.

    The easiest way around this is to not use <template> and instead use <script>:

    <script type="text/html" id="user-picker-template">
        <div class="user-picker"> ... </div>
    </script>