Bind existing viewmodel to view created by router


#1

Well consider this simple router:

import {Router, RouterConfiguration} from 'aurelia-router';
import {singleton} from "aurelia-dependency-injection";
import {Positions} from "./positions";


@singleton()
export class App {
    public router: Router;
    public positionVM: Positions;

    public constructor() {
         this.positionVM = new Positions(); //With a bit of extra additions.
    }

    public configureRouter(config: RouterConfiguration, router: Router) {
        config.title = 'Heren 5';
        config.map([
            { route: [ 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome'},
            { route: ['', 'position'], name: 'position', moduleId: './positions', nav: true, title: 'positions'},
            { route: 'test', redirect: 'http://www.google.com', nav: true, title: 'test' }
        ]);
        this.router = router;
    }
}

It is shown by the following html (not really important but just to allow testing):

<template>
  <require from="./nav-bar.html"></require>
  <require from="bootstrap/css/bootstrap.css"></require>
  <nav-bar router.bind="router"></nav-bar>
  <div class="page-host">
    <router-view></router-view>
  </div>
</template>

Now I also have a “positions.ts” and a “positions.html” file representing the viewmodel and view of the positions tab.

This “works”, except I can’t remember the state of the viewmodel when switching tabs. To solve this (and allow for future improvements) I wish to keep track of the viewmodel inside the app-class. Thus, instead of creating a new Positions each time the positions tab is opened, I wish let it refer to App.positionVM (Or better this.positionVM)

I’ve tried creating using a dynamic “navigation strategy”:

public configureRouter(config: RouterConfiguration, router: Router) {
    config.title = 'Heren 5';
    const navStrat = (instruction: NavigationInstruction) => {
          instruction.config.viewPorts = this.positionVM;
    };
    config.map([
        { route: [ 'welcome'], name: 'welcome', moduleId: './welcome', nav: true, title: 'Welcome'},
        { route: ['', 'position'], name: 'position', nav: true, title: 'positions', navigationStrategy: navStrat},
        { route: 'test', redirect: 'http://www.google.com', nav: true, title: 'test' }
    ]);
    this.router = router;
}

This however returns an error “ERROR [app-router] TypeError: Cannot read property ‘trim’ of undefined”. So it’s not the correct way to go around this?


#2

Actually you don’t need do any navigationStrategy.
You can just reference positionVM in all your page html templates.

The positionVM is carried over to every page loaded for router through overrideContext in binding context. Just have a look in the gist of this github issue https://github.com/aurelia/router/issues/548.

As long as you don’t use nested router (which has a bug of missing overrideContext as that github issue explains), you can access positionVM in all page html templates loaded by router.

If you want to access it in your component js code, implement bind(bindingContext, overrideContext){} and get it from overrideContext object.