0
votes

For a navigation menu, I have two groups of links, each group and link showing up or not dependent on a user's role. The roles are looked up when the link structure is being built and the list of links is built accordingly. The returned JSON gets parsed, put into observable arrays with no problem, but when I actually try and apply the bindings, the binding fails because the observables are blank. Here is the HTML...

<ul id="user-menu" class="menu" data-bind="foreach: areas">
    <li>
        <a data-bind="attr: { href: areaLink }">
            <img data-bind="attr: { src: iconUri }" />
            <span data-bind="text: areaName"></span>
        </a>
    </li>
</ul>
<ul id="admin-menu" class="menu" data-bind="foreach: adminAreas">
    <li>
        <a data-bind="attr: { href: areaLink }">
            <img data-bind="attr: { src: iconUri }" />
            <span data-bind="text: areaName"></span>
        </a>
    </li>
</ul>

Knockout view model in the background...

var navigation = (function() {

    function Area() {
        var self = this;

        self.areaName = ko.observable();
        self.areaLink = ko.observable();
        self.iconUri = ko.observable();
        self.sequenceNo = ko.observable();
        self.isAdmin = ko.observable();

        self.loadFromVM = function (vm) {
            self.areaName(vm.name || '');
            self.areaLink(vm.link || '');
            self.iconUri(vm.iconUri || '');
            self.sequenceNo(vm.sequenceNo || '');
            self.isAdmin(vm.isAdmin);
        }
    }

    function viewModel() {
        var self = this;

        self.areas = ko.observableArray([]);
        self.adminAreas = ko.observableArray([]);

        self.setup = function () {
            var data = {}; // population with basic session data

            $.getJSON('....php', { JSON.stringify(data) }, function (results) {
                for (var i = 0; i < results.length; i++) {
                    var area = new Area();

                    area.loadFromVM(results[i]);

                    if (area.isAdmin()) {
                        self.adminAreas().push(area);
                    } else {
                        self.areas().push(area);
                    }
                }
            });
        };
    }

    var vmInstance;

    return {
        setup: function () {
            vmInstance = new viewModel();

            vmInstance.setup();

            ko.applyBindings(vmInstance, $('#user-menu')[0]);
            ko.applyBindings(vmInstance, $('#admin-menu')[0]);
        }
    };

})();

And then I bring it together with this in the navigation view file...

    navigation.setup();

So after I get my JSON back, parse it, and organize it when I loop through the success function of the $.getJSON method, putting a watch on self.areas() and self.adminAreas() does show that the arrays have the exact information I want them to. But by the time they have to be applied, calling vmInstance.areas().length or vmInstance.adminAreas().length returns zero. Even more oddly, putting in an alert with the length of the arrays right after the $.getJSON call but within the setup() function will cause the alert to fire first, show zeroes, then goes through the get, populates the array, then fires zeroes again.

Not exactly sure what's going on here, but I can't remember seeing this kind of behavior in another project so I'm not quite sure what I'm doing wrong here. Any ideas or advice would be greatly appreciated.

EDIT: Nevermind on the Fiddle. It doesn't really capture my actual error.

1
code is valid so i think it is problem of ajax call because as it is async call so before response is available binding is done. try to debug in firebug. - Akhlesh
It's possible but I've had basically the same type of code work perfectly in other projects. The bindings should be getting updated as the async call comes in anyway and I see it trying in Chrome Developer Tools. - gfish3000
Ok @akhlesh, I take that back. After running out of all other options, I broke down and used the $.ajax call with async: false which did actually help. I'm thinking it worked several other times when I tried the same approach because the type was a GET though I'm not 100% sure which is why I wanted to rule out anything else first. - gfish3000
if it is an issue with timing (i.e. the Ajax return happens during the applyBindings call), you could just move the vmInstance.setup() call to after the applyBindings calls. They would then be rendered as empty, but then re-rendered when the data is added. - The Dark
it doesn't matter the getJSON async load data after your applyBindings, ko knows to update your view because areas and adminAreas are observableArray. To debug, you may add self.areas.subscribe(function(v) {console.log(v);}); (and also for adminAreas) to track the changes of the data. - huocp

1 Answers

0
votes

adminarea object is not initialized.you made the adminArea variable but instead of this you have used same area variable to set values.

    var adminArea = new Area();

    adminArea.areaName('test admin area'); 
    adminArea.areaLink('#');
    adminArea.iconUri('http://evernote.com/media/img/getting_started/skitch/windows8/win8-checkmark_icon.png');
    adminArea.sequenceNo(1);
    adminArea.isAdmin(true); 

Fiddle Demo