Durandal activate, detached events of child router never get triggered

When using the following Durandal child router plugin configuration, I'm not getting the detached and activate events described in the Durandal lifecycle, while other events work.

It is a single page application, with the childRouter controlling content of a div container within it. Each childRouter corresponds to a module and only one should be attached at a time, while others should be detached.

The application uses separate childRouterinstances for each module and I'm expecting the detached and activate events to be trigered each time navigation between two modules occurs (i.e. moduleOLD should get detached and moduleNEW should get attached).

Documentation says Durandal lifecycle events are triggered automatically:

Rows highlighted green will always execute when composing.

It doesn't work this way, which could be caused by:

  • Child routers not being notified about each other being attached/detached (i.e. need to subscribe to some event)
  • Child router working differently than parent router

It seems I have to detach something manually, or notify the router that is being replaced, to trigger detached and activate.

How to trigger the detached and activate events for childRouter?

define([ 'durandal/app', 'plugins/router'], function(
        app, router) {
    var childRouter = router.createChildRouter();
    childRouter.makeRelative({
        moduleId : 'modules/moduleX/pages',
        fromParent : true
    });
    childRouter.map([ {
        route : [ '', 'grid' ],
        moduleId : 'grid/index',
    }, {
        route : 'details/:id',
        moduleId : 'details/index',
    }, {
        route : 'details/tabs/base',
        moduleId : 'details/tabs/base',
    } ]);
    childRouter.buildNavigationModel();
    childRouter.activate = function() {
        console.log("activate");//DOESN'T WORK
    };
    childRouter.attached = function() {
        console.log("attached");//WORKS
    };
    childRouter.compositionComplete = function() {
        console.log("compositionComplete");//WORKS
    };
    childRouter.detached = function() {
        console.log("detached");//DOESN'T WORK
    };
    return {
        router : childRouter
    };
});

Output (no errors):

compositionComplete (gets called here too, when a route is first visited)
attached
compositionComplete

Answers:

Answer

In Durandal, activation lifecycle is specific to an activator. Each router, and child router, has one in activeItem, which also acts as an accessor to the currently activated module on that router.

Durandal ensures that the events from the lifecycle are properly called on each module when it changes or is about to change the active item on any of the currently active routers, and then when/if it binds the data to the module's view and attaches it to the DOM document.

To have the events triggered as intended, the methods with corresponding names should be declared on each module where such handler is required.

Activation takes place across all child routers, but happens in steps, starting from the root router. Thus, when picking a module for a new route, the root router will ask its currently active module if it canDeactivate, which will chain down to all of its active modules in the child routers. Then it will ask the new module if it canActivate. And if allowed by both, the activation will go through and the active item on the root router will change, triggerring the deactivate/activate methods on the modules, if present. If there is more to the route than the matched fragment and the activated module has a child router, the child routing on it will then be triggered as well.

When the same module is reused in the activation, the chain of events is still triggered, but it may happen so that the deactivate method on some of the modules activated in a child router may not trigger if that router doesn't receive a matched module from the new route. For example, it can happen if the child router isn't configured for a null route ('') and the navigation changes back to a parent route, say from products/item/15 back to products, leaving the child router with no new match and no new module to switch to, meaning that whatever parent module was matched for products will still have the old module for item 15 remain activated on its child router. To avoid such cases when reusing modules, one option is to always configure the null route ('') on child routers. If anything, match it to a module with a blank view, e.g. an empty div.

Tags

Recent Questions

Top Questions

Home Tags Terms of Service Privacy Policy DMCA Contact Us

©2020 All rights reserved.