Aurelia: How navigate between child routes

2020-06-18 03:25发布

问题:

I'm trying to navigate from one child route to another, but I continually get Route not found. My primary question: how to navigate between child views?

Below is the code, and I'll have additional questions below, too.

App Mode-View App Class:

export class App {
  configureRouter(config, router) {
    config.title = 'My Site';

    config.map([
      { route: ['','job-view'], name: 'jobView', moduleId: './job-view', nav: true, title:'Jobs'},
      { route: ['services'], name: 'services', moduleId: './services', nav: true, title:'Services'}
    ]);

    this.router = router;
    this.router.refreshNavigation();
  }
}

Q.2: Why do we need to save router here if it's always accessible from aurelia-router?

App Page:

<template>
  <require from='./nav-bar'></require>
  <nav-bar router.bind="router"></nav-bar>
  <div class="container">
      <router-view></router-view>
  </div>
</template>

Ok, so now that we have our root page view and nav defined, let's define the job-view MV.

JobView Class:

export class JobView {
  configureRouter(config, router) {
    config.map([
      { route: ['','jobs'], name: 'jobs', moduleId: './jobs', nav: false, title:'Jobs', settings:{icon:'glyphicon glyphicon-align-justify'} },
      { route: ['job/:id'], name: 'job', moduleId: './job', nav: false, title:'Job Details'}
    ]);

    this.router = router; //WHY SAVE THIS?
    this.router.refreshNavigation();
  }
}

JobView Page:

<template>

    <router-view></router-view>

</template>

Now, here are the child views. My assumption that is that routing that occurs should be relative to job-view. That's what I want, ideally.

Jobs Class (a bunch of code removed for brevity):

import {Router} from 'aurelia-router';

    @inject(Router)
    export class Jobs {

    constructor(router) {
      this.router = router;
    }

    toJob(id) {
      // this.router.navigateToRoute("job", {id:id}); // ERROR - ROUTE NOT FOUND
      this.router.navigate("#/job-view/job/".concat(id)); // THIS WORKS
    }
}

Q.3: I've seen both router.navigateToRoute and router.navigate referenced, but no indication when to use either or what the difference is, and the document doesn't seen to explain. Which should be used? Docs

Jobs Page: Details for jobs.html are irrelevant, so not listing them here.

Finally, the job view:

Job Class: Nothing relevant for job.js, so not listing code. At most I may perform navigation back to jobs, but that's handled below in the page.

Job Page:

<!-- a bunch of html //-->
<!-- HOW TO USE ROUTER IN HTML, NOT BELOW URL HREF? //-->
<a href="#/jobs">Print Jobs</a>
<!-- a bunch of html //-->

Q.4: Again, I'd like routing to relative, even in the HTML page. The above won't work, so what should I use?

There was a proposed answer in a similar question, but injecting job-view into job and using job-view's saved router didn't work either.

By the way, if I manually navigate to http://localhost:3000/#/job-view/job/3 the page loads fine, so it's clear something with the router.

Note to mod: A similar question was ask at How to access child router in Aurelia? but it wasn't answered with a solution that works.

回答1:

I will try to answer you questions one by one below.

I will start from Q2

Q.2: Why do we need to save router here if it's always accessible from aurelia-router?

So in your App Mode-View App Class you are referencing router property in your view: <nav-bar router.bind="router"></nav-bar>, that's why you need to declare the property to use it then. In the second view you are not so you don't need it :-)

The property router is also added when you need do something with the router in main.ts / main.js - the starting point of you application. This is because the router is configured for the first time there, and injection will not work in constructor, so you need to save this property to get it in configureRouter(..) method (note: this was a case before beta 1, I don't know if it's still there now).

In your code you have a call for this.router.refreshNavigation(); this will ensure that your router is updated with new information regarding current location of the routing.

Q.3: I've seen both router.navigateToRoute and router.navigate referenced, but no indication when to use either or what the difference is, and the document doesn't seen to explain. Which should be used? Docs

The method router.navigate(fragment: string, options?: any) uses an URL fragment not a route name to navigate, so e.g. router.navigate('#/app/child', {...<options - not params od the URL>...}). This method must be used to navigate absolutely between routers, and to access parent URL etc.

If you only are navigating around the current router you will always use router.navigateToRoute(route: string, params?: any, options?: any). This method is using a route name, not URL, co we just put there a name of route in the custom routing map (custom means the current child routing map, or current main routing regarding the URL location we are on the page). Here you can pass URL params in more convenient way, as you can see. You can use a params object instead of concatenating the URL with params.

Q.4: Again, I'd like routing to relative, even in the HTML page. The above won't work, so what should I use?

In Aurelia we are not using href attribute of the a tag directly for navigation. As already answered by Brandon, you have to use route-href attribute, which is probably nowhere documented just appears around on forums and portals. This is equivalent of the router.navigateToRoute(route: string, params?: any, options?: any), so you cannot use it to navigate between routers in such case you can use custom attribute or just use click.triger="navTo('#/app/child')", where the navTo() method is implemented in your View-Model and looks like this:

public navTo(routeName: string) {
    // Assuming you are injecting router somewhere in the constructor
    this.router.navigateToRoute(routeName);
}

And finally your topic question:

Q.1: How navigate between child routes

Probably now you know the answer, just use: router.navigate(fragment: string, options?: any) with absolute URL.

Below example custom attribute to solve this:

import {inject} from "aurelia-dependency-injection";
import {Router} from "aurelia-router";
import {customAttribute} from "aurelia-framework";

@customAttribute('nav-href')
@inject(Element, Router)
export class NavHref {
    private value: string = '';

    constructor(private element: Element, private router: Router) {
        let $this = this;
        element.addEventListener('click', () => {
            if ($this.value === 'back') {
                $this.router.navigateBack();
            } else {
                // expression
                $this.router.navigate($this.value);
            }
        });
    }

    public valueChanged(newValue: string, oldValue: string) {
        this.value = newValue;
    }
}

First you need to import it in your HTML, I named my file nav.href.ts:

<require from="common/nav.href"></require>

Then just use it in you HTML code:

<a nav-href="#/home/any-location">My link to any location</a>

Hope this will help you, cheers :-)



回答2:

The way I've configured child routers in my Aurelia apps is in this fashion:

app.js

export class App {
    configureRouter(config, router) {
        config.map([
            { route: ['','home'], name: 'home', moduleId: 'home', nav: true },
            { route: 'work', name: 'work', moduleId: 'work/work-section', nav: true },
        ]);
        this.router = router;
    }
}


work/work-section.js

export class WorkSection {

    configureRouter(config, router) {
        config.map([
            { route: '', moduleId: './work-list', nav: false },
            { route: ':slug', name: 'workDetail', moduleId: './work-detail', nav: false }
        ]);
        this.router = router;
    };

}

The corresponding "work-section.html" is simply a Router View:

<template>
    <router-view></router-view>
</template>

In this use case, I have my main app.js which defines a child router, "work", which sits in a subdirectory under src.

When the route /work is activated, the child router, "work-section" takes over, activating the "work-list" route, as the path segments end there: /work

"work-list.js" retrieves items from a REST API then passes the data to the view. From there, I'm able to use route binding to get to a "work detail" in the "work-list.html" view:

<div repeat.for="sample of item.samples">
    <a route-href="route: workDetail; params.bind: { slug: sample.slug }">
        ${sample.title}
    </a>
</div>

Hope that helps you out. I'm not 100% certain if you're asking how to do a redirect, or how to nav to a child route from the view, so please correct me if I'm wrong and I'll do my best to update my answer for you.



标签: aurelia