Search code examples
durandalactivator

durandal activators - how to create a module that implements activate() when it is loaded or first used


After reading the documentation about activators, I am still confused about how to use activators. What I am trying to do is simple:

I have defined a requireJS module called global. When I use this module in other modules, it's activate function is not called. I need it do be.

If I load a module using compose:, activate functions fire just fine. But if I just load it as an AMD module, they don't. This is all I want to fix.

Note: My module is a singleton, and I only need activate to run once actually, so I tried something like this, which doesn't work, my shell.js

define(['plugins/router', 'durandal/app', 'plugins/ajax','durandal/global'], function (router, app,ajax,global) {

    var vm = {};

    vm.global = global;

    vm.activate= function () {
        return vm.rebuildRouter();

//not sure what this does, does it return a promise, does it run global's activate, does it hook up global so it activates at a later time, and if so when?
        global.activator.activateItem(global); //I created an activator and called in activator inside global. Since its a singlton I think I only need one?

//Since I wasn't sure how to use activators I had to do this instead:
    var promise = global.activate();
        return promise;

//which works only the first time 
    }

I'd appreciate it if someone could explain this activators business to me. I use activate all the time with compose, and that works the way I want. I just don't understand what I need to do to get it to work as described above, and can't find a simple example that shows how to get any js module to behave the same way.


Solution

  • An activator is a special computed obseravble whose write function enforces the activation lifecycle when its value tries to change.

    The Activation Lifecycle

    1. canDeactivate is called on the current value
    2. canActivate is called on the value trying to be set
    3. deactivate is called on the current value
    4. activate is called on the value trying to be set

    Using an activator is done to force some object to fire off these events when its value changes, and block the change if canDeactivate or canActivate return false.

    It does not sound like you want need an activator. You aren't trying to enforce value changes happen according to these rules, you are trying to make sure a module performs some logic before it is used. It only needs to perform this logic once, as it is a singleton.

    Unfortunately RequireJS does not provide a way (that I know of) to wait for a module to perform async work.

    Here is one thing you could do. Since Durandal's app.start initialization is an asynchronous process, you could hook into it so that your call to setRoot comes after global has initialized.

    Global File

    define(['durandal/app', 'plugins/ajax', 'durandal/global'], function (app,ajax) {
        return function GlobalModule() {
            activate: function() {
                return //some promise;
            }
        };
    });
    

    Main.js

    define(['durandal/system', 'durandal/app', 'durandal/global'],
    function (system, app, global) {
        app.configurePlugins({ /* plugins */ });
    
        app.title = 'App';
        app.start()
            .then(global.activate)
            .then(function () {
                app.setRoot('shell');
            });
    });
    

    This will ensure that setRoot doesn't start until global has finished activating. It should then be safe in any of your app code to use it.

    Please keep in mind I have no idea what durandal/global is, and this work depends on activate being a promise returning function.