Search code examples
jqueryknockout.jsrequirejscrossroadsjs

Simultaneous external template loading and data binding with Knockout and Require


I want to send both requests for view and data, then when view and data requests responded they need to be binded. I use setInterval for this. But I don't know this is the correct way or not.

Sometimes I need to update most of ViewModel data. I can't figure out how can I update these mass data.

With this code I get both view and data. But I can't use data in viewModel. Because if I try to update data after binding I don't know what to do.

Any suggestions?

in app.js

crossroads.addRoute('/cart', function () {
    require(['cart'], function () {
        cart.init();
    });
});

in cart.js

define(['jquery', 'knockout'], function($, ko) {
    var cart = {
        viewLoaded: false,
        dataLoaded: false,
        template: '',
        cartData: {},

        container: '#container',

        // Gets external template
        getView: function () {
            $.ajax({
                ...
                success: function (response) {
                    cart.viewLoaded = true;
                    $(cart.container).html(response);
                }
            });
        },

        // Gets data
        getCart: function () {
            $.ajax({
                ...
                success: function (response) {
                    cart.dataLoaded = true;
                    cart.cartData = response;
                }
            });
        },

        ViewModel: function () {
            var self = this;

            self.cartId = ko.observable();
            self.products = ko.observableArray([]);
            .
            .
            .
        },

        // Checks both template and data are loaded
        bindings: function () {

            var loadControl = setInterval(function() {
                if (cart.viewLoaded && cart.dataLoaded) {
                    ko.applyBindings(new cart.ViewModel(), $(cart.container)[0]);
                    clearInterval(loadControl);
                }
            }, 100);
        },

        init: function () {
            this.getView();
            this.getCart();
            this.bindings();
        }
    };

    return cart;
});

Solution

  • I used callback function instead of interval. And when user change URL I remove DOM elements and bindings like this:

    in app.js

    var stepCart = crossroads.addRoute('/cart', function () {
        require(['cart'], function () {
            cart.init();
        });
    });
    
    stepCart.switched.add(function () {
        cart.stopEvents();
    });
    

    in cart.js

    define(['jquery', 'knockout'], function($, ko) {
        var cart = {
                .
                .
                .
    
            // Gets external template
            getView: function (callback) {
                $.ajax({
                    ...
                    success: function (response) {
                        cart.viewLoaded = true;
                        $(cart.container).html(response);
    
                        if (typeof callback === 'function') {
                            callback();
                        }
                    }
                });
            },
    
            // Gets data
            getCart: function (callback) {
                $.ajax({
                    ...
                    success: function (response) {
                        cart.dataLoaded = true;
                        cart.cartData = response;
    
                        if (typeof callback === 'function') {
                            callback();
                        }
                    }
                });
            },
    
                .
                .
                .
    
            // Removes DOM elements and bindings
            stopEvents: function () {
                ko.cleanNode($(cart.container).get(0));
                $(cart.container).empty();
                this.viewLoaded = false;
                this.dataLoaded = false;
            },
    
            // Checks both template and data are loaded
            bindings: function () {
    
                if (cart.viewLoaded && cart.dataLoaded) {
                    ko.applyBindings(new cart.ViewModel(cart.cartData), $(cart.container)[0]);
                }
            },
    
            init: function () {
                this.getCart(cart.bindings);
                this.getView(cart.bindings);
            }
        };
    
        return cart;
    });