1
votes

This could perhaps be a question of code structure rather than a question about the framework.

Basically I am wondering if the activate function on the parent router should be called every time one of its child routes are navigated to?

My goal: Have a module which returns a child router, this is the parent router. This parent router is in charge of creating an object if the user is adding something new, or retrieving data from the server if the user selects something which already exist. The child routes modules simply get the object created or retrieved via the parent router module, and provide form inputs to manipulate that object (via ko data-binding). The issue I am having is the parent router's activate function is getting called each time the child routes are navigated to, which is where the creation/retrieval logic is and causes the object to be retrieved once again, causing any data manipulation to be overwritten.

Below is some code to hopefully better illustrate:

// ParentRouter
define([...], function(...) {
    var selectedObj = ko.observable(null),
        configRouter = router.createChildRouter()
            .makeRelative({
               moduleId: 'viewmodels',
               fromParent: true,
               dynamicHash: ':id'
            }).map([
                {
                    route: ['Settings 1', ''],
                    moduleId: 'SettingsOne',
                    nav: true
                },
                {
                    route: 'Settings 2',
                    moduleId: 'SettingsTwo',
                    nav: true
                },
                {
                    route: 'Settings 3',
                    moduleId: 'SettingsThree',
                    nav: true
                }
            ]).buildNavigationModel();

    function getSelectedObj() {
        return selectedObj;
    }

    return {
        router: configRouter,

        obj: selectedObj, // privileged property which is accessed by other modules via the getSelectedObj method, this prop gets set in the activate function depending on the param
        getSelectedObj: getSelectedObj, // method which returns the current selected obj

        activate: function (objId) {

            if (objId == 'New') {

                 // create a new object with default props

            } else {

                // retrieve the data for the selected obj from API via the 'objId' param

            }

        }
    }
});



// SettingsOne module - child route module

define(['viewmodels/parentRouter'], function(parentRouter) {

    // this module simply gets a property from the parent routers module and binds it to form inputs in its View (which update automatically) so the user can edit information on that obj


    var obj = parentRouter.getSelectedObj(); // get the current selected obj via parent routers module method

    return {
        obj: obj
    }
});


Below is the shell module of my app, showing the main application router. 
// Shell.js
define([
    'plugins/router'
], function (router) {
    var routes = [
            {
                route: '',
                moduleId: 'viewmodels/home'
            },
            {
                route: ':id*configs',
                moduleId: 'viewmodels/parentRouter'
            }
        ];

    return {
        router: router,
        activate: function () {
            return router.map(routes)
                    .mapUnknownRoutes('viewmodels/notfound', 'not-found')
                    .activate();
        }
    }
});
2

2 Answers

0
votes

I don't think there's anything wrong with activating parent every time before activating child. If you don't want retreval to reoccur, make it conditional, for example:

define(function (require) {
    var
        $ = require('jquery'),
        Q = require('q'),

        activate = function () {
            return load();
        },

        loadPromise = null,

        load = function () {
            return loadPromise || (loadPromise = Q($.get('/my/api'))
                    .then(function (data) {
                        //use data;
                    }));
        };

        return {
           activate: activate
        }
});
0
votes

I am facing a similar issue where I don't want my parent vm to have activate called on it again. From the durandal Doc's it sounds like this should be supported:

From Module Reuse heading here

If the module has a child router associated with it. The instance will be kept.

That said I haven't been able to get this to work myself, but I think it goes towards you question. It appears that Durandal does attempt to support this functionality, but there is some nuance that we are both missing. If I figure it out I'll update here.