0

I have a viewmodel:

define(['knockout'], function (ko) {
        var self, albumWrapper;

        albumWrapper = function () {
            self = this;

            // get album name from url parameter
            var albumName = routerWrapper.router.activeInstruction().params[0];
            self.retrieveAlbum(albumName);
        };

        albumWrapper.prototype = {
            retrieveAlbum: function (name) {
                /* do something to retrieve data */
            }
        }

        return new albumWrapper();
    });

I config route to go to this view by this url: #gallery/album/:name

But when parameter (/:name) changed by this code:

window.location.href = '#gallery/album/' + data.name();

This viewmodel just occur first time, so I cannot retrieve any new album when. I think this problem because of cache view, but I not sure. Please help me fix this. I use durandal in this case.

Stiger
  • 1,189
  • 2
  • 14
  • 29

2 Answers2

2

I don't think your problem is caching, I think it has to do with the way you setup your view model. From what I see, durandal is going to resolve your view the first time and use your code above to create the viewmodel. Returning new albumWrapper() will cause your viewmodel to be a singleton. The constructor function is only going to be called on first time and never again. You can try removing the new keyword before albumWrapper() to return the function instead of an new instance of it. This way durandal will create a new view model each time which should cause your code in the constructor to be run each time as well. Although a better way might be to use the activate function and let the module life cycle work for you. See the changes below:

define(['knockout'], function (ko) {
    var self, albumWrapper;

    albumWrapper = function () {
        var self = this;

        self.CurrentAlbum = ko.observable();
    };

    albumWrapper.prototype = {
        retrieveAlbum: function (name) {
            /* do something to retrieve data */
        },
        activate: function(name) {
            var self = this;    
            var selectedAlbum = self.retrieveAlbum(name);
            self.CurrentAlbum(selectedAlbum);
        }
    }

    return albumWrapper();
});

Now durandal should get one instance of your view but call the activate function with any route parameters each time the route is requested. Also added is an observable field called Current Album that you can bind your view elements to. Navigating away from the route does not cause the instance of the view to be removed from memory like a web page, it just sits there till the next time you navigate to the route again, more like a desktop app trading out screens. The activate function is called on every navigation and lets you setup the view for new album name on the route. Assuming the retrieveAlbum function returns an album object with properties like name, date, artist, etc, you can set that as the value of Current Album observable which will automatigically update the elements on your view. Check out the docs on routing and the view life cycle here:

http://durandaljs.com/documentation/Using-The-Router.html

http://durandaljs.com/documentation/Hooking-Lifecycle-Callbacks.html

Frank
  • 2,178
  • 2
  • 17
  • 24
  • if route don't config param like `#gallery/album` only, it seems `activate` function doesn't work. Can we make it works in this case? – Stiger Mar 29 '14 at 17:02
  • Sorry, it doesnot work when it inside an template viewModel binding. – Stiger Mar 29 '14 at 17:18
  • 1
    for #gallery/album to work you need to define multiple routes for your module: router.map([{ route: ['gallery/album/:name', 'gallery/album/:name'], moduleId: 'private/dashboard', title: 'Dashboard' }]). you can pass an array for the route property. As for "an template viewModel binding", I am not sure what you are referring to, can you explain more with an example? – Frank Mar 30 '14 at 18:19
  • so, how do you disable cache? – pilavdzice Apr 21 '14 at 21:41
  • @pilavdzice Well that depends, you can use an [option](http://stackoverflow.com/questions/8315088/prevent-requirejs-from-caching-required-scripts) on require js to append a query string to all the scripts it loads to force the browser to get a new copy of your js files. Or you can make your [models return a function](http://durandaljs.com/documentation/Creating-A-Module.html) and act as a factory instead of returning an object and being a singleton – Frank Apr 23 '14 at 14:23
1

Posting this for others that may be looking for how to disable durandal cache.

You can set cacheViews to false like this in your module to stop caching, like this singleton module:

var vm = {};
vm.cacheViews = false;
return vm;

Or in the case of the example above:

    albumWrapper = function () {
        self = this;
        self.cacheViews = false;
        // get album name from url parameter
        var albumName = routerWrapper.router.activeInstruction().params[0];
        self.retrieveAlbum(albumName);
    };
pilavdzice
  • 958
  • 8
  • 27
  • True this will work but if you are concerned about migrating to the next version of Durandal (2.0 on Angular) then you want to avoid singleton object modules and use function modules instead. see [Rob's blog post](http://eisenbergeffect.bluespire.com/preparing-for-durandal-nextgen/) – Frank Apr 28 '14 at 19:13