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

StackOverflow https://stackoverflow.com/questions/22793692

  •  25-06-2023
  •  | 
  •  

Question

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.

Was it helpful?

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.

OTHER TIPS

You have many incorrect assumptions in your question... You are mixing up AMD module resolution (done by require.js) and view composition (a durandal concept) as if they are the same thing.

Anyways, I think you are headed down the wrong path here with activators (as they are a separate concept from simple AMD module resolution) so I'm going to sidestep the actual question, and answer what I think your question should probably be, which is: How can I add automatic initialization logic to an AMD module?

Given that you only want the initialization to occur once, that says to me that you also want the module to be a singleton.

Either way, I can think of no finer place to implement initialization logic than in the constructor function of the module.

Basically you want to implement your AMD module as a constructor function, put your init logic in there, and leverage a singleton pattern so that the initialization logic only happens once.

This might be a useful resource for you in doing so:

http://unscriptable.com/2011/09/22/amd-module-patterns-singleton/

Apologies if I've miss-read what you're trying to do here.

I eventually accomplished what I needed, but is this the best way and/or how this is intended to be done? I think this isn't ideal because I need all this code in every single module, for every module I include:

var viewModel = {}; viewModel.global = activator.create();

viewModel.activate = function () {

    return $.when(viewModel.global.activateItem(global),
               ... other ajax calls and stuff
               ))
               .then(function () {
                    //stuff I used to have in my activate function before, that needs global to already exist
               });
};

Is this really how to properly use an activator? What I have above works, but as someone pointed out I could just call global.activate() instead of activateItem(global) and I wouldn't need an activator at all.. so, what's the purpose of it?

When you compose something, the activation lifecycle happens on its own. I am trying to build a module that has a lifecycle as well, even if I am not composing it, is that possible?

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top