Search code examples
javascriptknockout.jsknockout-components

Knockout 3.2 amd components not updating observables


I am having trouble getting observable arrays to update using knockout 3.2 with components and require. I can manually push items into the array in the view model at declaration no problem, however when they are pushed in via an ajax call OR via a hard coded push on a button click the DOM does not update.

Debugging, I can see the array has the items in, however the DOM is not updating. Any help would be greatly appreciated.

Default.html

<!-- ko if: state() === 'home' -->
    <template></template>
<!-- /ko -->

Template.html

<table id="items">
    <thead>
        <tr>
            <th>Category</th>
            <th>Item</th>
            <th>Cost</th>
        </tr>
    </thead>
    <tbody data-bind="foreach: Items()">
        <tr>
            <td data-bind="text: CategoryName"></td>
            <td data-bind="text: Name"></td>
            <td data-bind="text: '£' + Cost"></td>
        </tr>
    </tbody>
</table>

Startup.js

var appStateViewModel = {
    isRunning: ko.observable(false),
    state: ko.observable('home'),
    allowLog: false
};

// Configure requirejs
require.config({
    paths: {
        text: 'Scripts/text',
        knockout: '//localhost:2222/Scripts/Plugins/knockout'
    },
    urlArgs: "bust=" + (new Date()).getTime()
});

// Register knockout components
ko.components.register('template', { require: './Modules/Template/Template' });

// Apply bindings for state
var scope = document.getElementById('app');
ko.applyBindings(appStateViewModel, scope);

Template.js

define(['knockout', 'text!./Template.html'], function (ko, htmlString) {
    function TemplateViewModel(params) {

        var self = this;
        self.Items = ko.observableArray();

            $.getJSON("Items")
                .done(function (response) {

                    $.each(response, function (i, item) {
                        self.Items.push({
                            Id: item.Id,
                            Name: item.Name,
                            Description: item.Description,
                            Cost: item.Cost,
                            CategoryName: item.CategoryName
                        });
                    });
                })
                .fail(function (listResponse, status, errorThrown) {
                    alert(errorThrown);
                });
    }

    // Return component definition
    return { viewModel: TemplateViewModel, template: htmlString };
});

Solution

  • I have found a fix. It appears I had not set up require properly.

    The fix:

    require(["knockout", "jquery", "text"], function (ko) {
    
        // Register knockout components
        ko.components.register('template', { require: './Modules/Template/Template' });
    
        // Apply bindings for state
        var scope = document.getElementById('app');
        ko.applyBindings(appStateViewModel, scope);
    
    });