Durandal.js 2.0 Set document title within activate method
Asked Answered
A

3

6

In my shell, I have set up my routes like so:

router.map([
            { route: '', title: 'Search', moduleId: 'viewmodels/search/search' },
            { route: 'location/:paramX/:paramY', title: 'Location', moduleId: 'viewmodels/location/location' }
        ]).buildNavigationModel();

I have an activate method like so:

activate: function(paramX, paramY) {
    // TODO: set document title
    // TODO: do something with input params    
}

For the location page, the document title is set to the Location | [Name of my app]. I would like to change this to be made up from the params taken in the activate method (paramX, paramY) on my activate method for the location page. How do I do this?

Antigone answered 10/9, 2013 at 8:38 Comment(0)
R
10

You can achieve this by overriding the default behaviour of the process of the router to set the title.

The title is always set after the navigation is complete so the activate method of your viewmodel has been called before. The current implementation in Durandal 2.0 is:

   router.updateDocumentTitle = function(instance, instruction) {
        if (instruction.config.title) {
            if (app.title) {
                document.title = instruction.config.title + " | " + app.title;
            } else {
                document.title = instruction.config.title;
            }
        } else if (app.title) {
            document.title = app.title;
        }
    };

This is called in the method completeNavigation in the router.js.

In instance param you have the ViewModel that you are activating so a possible solution could be to override the updateDocumentTilte function in shell.js or main.js and use the instance to get the values that you want. For example you could do something like this (make sure you have the app and the router instance):

 router.updateDocumentTitle = function (instance, instruction) {
            if (instance.setTitle)
                document.title = instance.setTitle();
            else if (instruction.config.title) {
                if (app.title) {
                    document.title = instruction.config.title + " | " + app.title;
                } else {
                    document.title = instruction.config.title;
                }
            } else if (app.title) {
                document.title = app.title;
            }
        };

In this code we check if the instance (the current ViewModel) contains a method setTitle, if it does then we get the title calling the function. Then in our viewmodel we can have something like:

define(function () {

var id;
var vm = {
    activate: function (param) {
        id = param;
        return true;
    },
    setTitle: function () {
        return 'My new Title ' + id;  //Or whatever you want to return
    }
};

return vm;
});

If your viewmodel does not contain this method, then it should fall to the current behaviour.

Rozalin answered 10/9, 2013 at 10:45 Comment(5)
Great, that works :) I just had to modify your code a little:Antigone
Cool! Yep, I noticed before.. I added an else after the first if. Glad it worked!Rozalin
updateDocumentTitle doesn't fire when changing the value of the observable inside it. I'm using the observable because my title is dependent on some information I get after an ajax call.Evert
In which time of the composition do you do the ajax call? On activate? setTitle should be called after activate callback.Rozalin
Make sure you override the function BEFORE returning the viewmodel in shell. Otherwise it won't work. Good answer btw!Uncommercial
R
3

Here's how I achieved it:

    activate: function (product, context) {
        // Update the title
        router.activeInstruction().config.title = "Buy " + product;
        ...
        ...
        ...
        ...

It works, but I don't know if that's the approved method.

Rally answered 13/2, 2014 at 20:52 Comment(1)
This seems a lot simpler than the accepted answer, and doesn't involve overwriting any Durandal functionsMisappropriate
A
0

I needed to use observables for this, because the data that the title is derived from is loaded by AJAX in the activate method.

So I put this in my application bootstrap code:

var originalRouterUpdateDocumentTitle = router.updateDocumentTitle;
router.updateDocumentTitle = function (instance, instruction) {
    if (ko.isObservable(instance.documentTitle)) {
        instruction.config.title = instance.documentTitle;
    }
    return originalRouterUpdateDocumentTitle(instance, instruction);
};

If the view model has an observable named documentTitle, it is copied to the instruction.config.title. This is then bound to the actual document.title by Durandal (using a subscription), so that whenever the value of the documentTitle observable changes, the document.title changes. The documentTitle observable could be a plain observable or a computed observable.

This approach also delegates most of the work to the actual router.updateDocumentTitle() method, by intercepting and modifying the instruction value based on instance, and then calling through to originalRouterUpdateDocumentTitle.

This works with Durandal 2.1.0.

Arquebus answered 1/4, 2015 at 9:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.