2
votes

This works well for getting a sub menu to work within a main view.

I was able to get the activate() function to work for the sub menu viewmodels by adding activate: true to the compose binding. The other life cycle events are not firing though. How do I get canActivate(), canDeactivate() and deactivate() to work within my sub viewmodels?

Refer to the link at the top of my post for the source code. The answer to this linked question contains an example that I've used to integrate this functionality into a Hot Towel project.

2

2 Answers

2
votes

Looks like the way @evan-larsen constructed the inAbout activator() prevents the execution of the canDeactivate and Deactivate events. Not sure why yet.

By converting it to use system.acquireinstead I was able to make those events fire again.

return system.acquire(convertSplatToModuleId(activationData.splat)).then(function( Sample ) {
       App.inAbout(new Sample());
   });

Here's the modified about/index.js.

define(
  ['durandal/system', 'durandal/viewModel', 'durandal/plugins/router'],
  function( system, viewModel, router ) {
      var defaultPage = 'aboutUs';

      function convertNameToModuleId ( name ) {
          return 'deepLinkingExample/areas/about/' + name + '/' + name;
      }

      function convertSplatToModuleId ( splat ) {
          if ( splat && splat.length > 0 ) {
              return convertNameToModuleId(splat[0]);
          }
          return convertNameToModuleId(defaultPage);
      }

      var App = {
          inAbout: viewModel.activator(),

          activate: function( activationData ) {

                  return system.acquire(convertSplatToModuleId(activationData.splat)).then(function( Sample ) {
                      App.inAbout(new Sample());
                  });
          },

          showPage: function( name ) {
              return function() {
                  router.navigateTo('#/about/' + name);
              };
          },

          isPageActive: function( name ) {
              var moduleName = convertNameToModuleId(name);
              return ko.computed(function() {
                    return this.inAbout().__moduleId__ === moduleName;
              }, this);
          }
      };

      return App;
  }
);

The code above assume that your returned detail VMs are ctors. An aboutMe.js like the below should do it.

define(['durandal/app', 'durandal/system'], function (app, system) {

    var ctor = function() {
           this.name = "About me";
           this.description = "For demonstration only";
       };

       ctor.prototype.canActivate = function () {
           return app.showMessage('Do you want to view ' + this.name + '?', 'Master Detail', ['Yes', 'No']);
       };

       ctor.prototype.activate = function() {
           system.log('Model Activating', this);
       };

       ctor.prototype.canDeactivate = function () {
           return app.showMessage('Do you want to leave ' + this.name + '?', 'Master Detail', ['Yes', 'No']);
       };

       ctor.prototype.deactivate = function () {
           system.log('Model Deactivating', this);
       };

       return ctor;
});
0
votes

According to the docs activate: true won't add the complete life cycle events

Note: Cases 3 and 4 are a bit different as they only enforce canActivate and activate callbacks; not the deactivation lifecycle. To enable that, you must use a full activator yourself (cases 1 or 2).

So you have to create a full activator that controls the sub menu. Take a look at https://github.com/BlueSpire/Durandal/blob/master/App/samples/knockout/index.js#L6 for an example.

In short:

define(['durandal/viewModel'], function (viewModel) {

    return {
        activeSample:viewModel.activator(),
        ...