jcc
jcc

Reputation: 1147

View not updating bindings in SPA with Durandal/Knockout

I am building a SPA app with the default Durandal setup. I have multiple views returning data with ajax calls however, it is not working perfectly. I created my shell page with a search box so I can search through a list of employees shown here.

Shell.js

define(['require', 'durandal/plugins/router', 'durandal/app', 'config'],
function (require, router, app, config) {

var shell = {
    router: router,
    searchData: searchData,
    employees: ko.observable(),
    search: search,
    activate: activate,
};

var searchData = ko.observable('');

function search(searchData) {
    var url = '#/employeeSearch/' + searchData.searchData;
    router.navigateTo(url);
}

return shell;

function activate() {

    router.map(config.routes);
    return router.activate(config.startModule);
}

});

shell.html

<div class="input-group">
            <input type="text" class="form-control" placeholder="Search Employees" data-bind="value: searchData" />
            <span class="input-group-btn">
                <button type="submit" class="btn btn-default" data-bind="click: search">Search</button>
            </span>
        </div>

The user puts in a search and when they click the search button the view below navigates to the employeeSearch page. This does work and return the data and view I need it to here.

define(['require', 'durandal/plugins/router', 'durandal/app', 'config', 'services/logger'],
function(require, router, app, config, logger) {

    var goBack = function() {
        router.navigateBack();
    };

    function details(employee) {
        var url = '#/employee/' + employee.Id + '/profile';
        router.navigateTo(url);
    }

    var vm = {
        goBack: goBack,
        employees: ko.observable(),
        details: details,
    };

    return {
        activate: function (route) {
            var self = this;
            return self.getEmployees(route.q);
        },
        getEmployees: function (query) {
            return $.ajax(app.url('/employees?q=' + query),
            {
                type: "GET",
                contentType: 'application/json',
                dataType: 'json',
            }).then(querySucceeded).promise;

            function querySucceeded(result) {
                self.employees = result;
                logger.log(query + ' Search Activated!', null, 'employeeSearch', true);
            }
        },
    };
});

So then, if I try to search for another name, the url will navigate, the logger will show the value I searched for, however the view itself will not show the new results. Exploring in the Chrome debugger, I view the employees object to contain the new result set, but the view has still not updated. If I refresh the page however, the view does properly show up I have viewed multiple questions on here with similar issues about keeping your data calls in the activate method, make sure the data returns a promise before completing the activate method, and putting DOM manipulation in the viewAttached. Javascript is not rendering in my SPA How to use observables in Durandal? HotTowel: Viewmodel lose mapping when navigating between pages

After putting those practices in my code, I am still having problems getting the view to update correctly.

Are there any other Durandal/Knockout properties I need to be aware of? How can I get the bindings to update every time I navigate to a view with (router.navigateTo(url);). I have set my shell properties (cacheViews: true) to true and false but nothing seems to change.

This is one of many problems I have been having with building SPA apps with Durandal. Trying not to give up yet.

Upvotes: 1

Views: 1586

Answers (2)

shai
shai

Reputation: 111

In your samples you have not shown/mentioned where knockout is being loaded. If you are using Durandal 2.0 then add the following line to the top of your main.js file above your existing define statement

define('knockout', ko);

Upvotes: 0

Stefan B.
Stefan B.

Reputation: 384

I cant test this quick but a think you handle the observable wrong.

I suspect the "result" var is an array of employees. In this case you might handle this with an observableArray (http://knockoutjs.com/examples/collections.html)

And you cant set the value directly like self.employees You must call the observable function to set the value like

 function querySucceeded(result) {
                self.employees(result) 
                logger.log(query + ' Search Activated!', null, 'employeeSearch', true);
            }

Upvotes: 1

Related Questions