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? //-->
Print Jobs
<!-- 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.
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 :-)
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.
Related
I have encountered a weird case when using VueX and Vue-Router and I am not too sure how to cleanly solve it.
I have a component (let's call it "ComponentWithStore") that registers a named store module a bit like this : (the actual content of the store don't matter. obviously in this toy example using VueX is overkill, but this is a very simplified version of a much more complexe app where using VueX makes sense)
// ComponentWithStore.vue
<script>
import module from './componentStore.js';
export default {
name: 'ComponentWithStore',
beforeCreate() {
this.$store.registerModule(module.name, module);
},
beforeDestroy() {
this.$store.unregisterModule(module.name);
}
}
</script>
Then I place this component in a view (or page) which is then associated to a route (let's call this page "Home").
// Home.vue
<template>
<div class="home">
Home
<ComponentWithStore/>
</div>
</template>
<script>
import ComponentWithStore from '#/components/ComponentWithStore.vue';
export default {
name: "Home",
components: { ComponentWithStore }
};
</script>
So far so good, when I visit the Home route, the store module is registered, and when I leave the Home route the store module is cleaned up.
Let's say I then create a new view (page), let's call it "About", and this new About page is basically identical to Home.vue, in that it also uses ComponentWithStore.
// About.vue
<template>
<div class="about">
About
<ComponentWithStore/>
</div>
</template>
<script>
import ComponentWithStore from '#/components/ComponentWithStore.vue';
export default {
name: "About",
components: { ComponentWithStore }
};
</script>
Now I encounter the following error when navigating from Home to About :
vuex.esm.js?2f62:709 [vuex] duplicate namespace myComponentStore/ for the namespaced module myComponentStore
What happens is that the store module for "About" is registered before the store module for "Home" is unregistered, hence the duplicate namespace error.
So I understand well what the issue is, however I am unsure what would be the cleanest solution to solve this situation. All ideas are welcome
A full sample may be found here : https://github.com/mmgagnon/vue-module-router-clash
To use, simply run it and switch between the Home and About pages.
As you have mentioned, the issue is due to the ordering of the hooks. You just need to use the correct hooks to ensure that the old component unregisters the module first before the new component registers it again.
At a high level, here is the order of hooks in your situation when navigating from Home to About:
About beforeCreate
About created
Home beforeDestroy
Home destroyed
About mounted
So you can register the module in the mounted hook and unregister it in either beforeDestroy or destroyed.
I haven't tested this though. It might not work if your component requires access to the store after it is created and before it is mounted.
A better approach is to create an abstraction to register and unregister modules that allows for overlaps.
Untested, but something like this might work:
function RegistrationPlugin(store) {
const modules = new Map()
store.registerModuleSafely = function (name, module) {
const count = modules.get(name) || 0
if (count === 0) {
store.registerModule(name, module)
}
modules.set(name, count + 1)
}
store.unregisterModuleSafely = function (name) {
const count = modules.get(name) || 0
if (count === 1) {
store.unregisterModule(name)
modules.delete(name)
} else if (count > 1) {
modules.set(name, count - 1)
}
}
}
Specify the plugin when you create your store:
const store = new Vuex.Store({
plugins: [RegistrationPlugin]
})
Now register and unregister your modules like this:
beforeCreate() {
this.$store.registerModuleSafely(module.name, module)
},
destroyed() {
this.$store.unregisterModuleSafely(module.name)
}
I have a bit of a pickle.
I am using Route guard (implementing CanActivate interface) to check if user is granted access to particular route:
const routes: Routes = [
{
path: '',
component: DashboardViewComponent
},
{
path: 'login',
component: LoginViewComponent
},
{
path: 'protected/foo',
component: FooViewComponent,
data: {allowAccessTo: ['Administrator']},
canActivate: [RouteGuard]
},
{
path: '**',
component: ErrorNotFoundViewComponent
}
];
Now it works great in protecting the '/protected/foo' route from activating, but I would like to tell the user that route he is trying to access is forbidden (similar to 403 Forbidden you may get from server).
The problem:
How do I show the user this special error view without redirecting him to error route which seams to be the preferred option by so many sources I have found?
And how do I still use my RouteGuard without actually loading the forbidden route, because if I check access inside my FooViewComponent and display different view it kind of defeats point of having RouteGuard in the first place.
Ideally I would like to have my RouteGuard not only returning false in canActivate() method, but also replace component completely with say ErrorForbiddenViewComponent. But I have no idea how to do it, or is it event possible. Any alternatives?
This is how my route guard looks now:
import {Injectable} from '#angular/core';
import {Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from '#angular/router';
import {AuthService} from '../services/auth.service';
#Injectable()
export class RouteGuard implements CanActivate {
constructor(
private router: Router,
private auth: AuthService
) {}
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
const { auth, router } = this;
const { allowAccessTo } = next.data;
const identity = auth.getIdentity();
if (
identity &&
allowAccessTo.indexOf(identity.role)
) {
// all good, proceed with activating route
return true;
}
if (identity) {
// TODO show ErrorForbiddenViewComponent instead of redirecting
console.log('403 Forbidden >>', next);
}
else {
// not logged in: redirect to login page with the return url
const [returnUrl, returnQueryParams] = state.url.split('?');
console.log('401 Unauthorised >>', returnUrl, returnQueryParams, next);
router.navigate(['/login'], {queryParams: {returnUrl, returnQueryParams}});
}
return false;
}
}
So I am just preventing route from loading, but I am not redirecting. I only redirect non logged visitors to login route.
Reasoning:
Routes should reflect certain state of application - visiting a route
url should recreate that state
To have error routes (except for 404 Not Found) would mean your application can actually recreate error states. This makes no sense
as why would you keep error state as state of your application? For
debugging purpose one should use logs (console or server), revisiting
error page (i.e. page refresh) might interfere with that.
Also by redirecting to error route app should provide some insights of error to user. For that matter either some parameter would need to
be passed via url or (far worse) keeping the error sate in some error
service and retrieve it upon accessing error route.
Also, ignoring the RouteGuard and just loading the component and checking access inside it may result in some extra dependencies
loaded which would not be used anyway (as user is not allowed), makes
the whole lazy loading much harder.
Does anyone have some kind of solution for this? I also wonder how come that after Angular 2+ being around for so long nobody had this kind of situation before? Everybody is just ok with redirecting?
Also keep in mind that although I am currently using the FooViewComponent synchronously, that may change in future!
I had once worked on the similar problem.
Sharing my stackblitz poc where I have created -
Authenticated Component (with guard)
Login Component
Permission Guard
Route (/auth route is provided with PermissionGuardService guard)
The guard is evaluating the user type and handling the redirection / error accordingly.
The use cases are -
User is not logged in (shows a toast with log in message)
User is not admin (shows a toast with unauthorised message)
User is admin (show a toast with success messaage)
I have stored the user in local storage.
EDIT - DEMO
Let me know if you need a special handling in it and I will update the code base.
Cheers!
After looking at angular2 example provided by Tarun Lalwani in comments of question and after taking deeper look into Dynamic component loader article on Angular docs I have managed to apply it to my code:
I no longer use my RouteGuard when specifying routes:
{
path: 'protected/foo',
component: FooViewComponent,
data: {allowAccessTo: ['Administrator']}, // admin only
canActivate: [RouteGuard]
},
Instead I have created special RouteGuardComponent and here is how I use it:
{
path: 'protected/foo',
component: RouteGuardComponent,
data: {component: FooViewComponent, allowAccessTo: ['Administrator']}
},
This is the code of RouteGuardComponent:
#Component({
selector: 'app-route-guard',
template: '<ng-template route-guard-bind-component></ng-template>
// note the use of special directive ^^
})
export class RouteGuardComponent implements OnInit {
#ViewChild(RouteGuardBindComponentDirective)
bindComponent: RouteGuardBindComponentDirective;
// ^^ and here we bind to that directive instance in template
constructor(
private auth: AuthService,
private route: ActivatedRoute,
private componentFactoryResolver: ComponentFactoryResolver
) {
}
ngOnInit() {
const {auth, route, componentFactoryResolver, bindComponent} = this;
const {component, allowAccessTo} = route.snapshot.data;
const identity = auth.getIdentity();
const hasAccess = identity && allowAccessTo.indexOf(identity.role);
const componentFactory = componentFactoryResolver.resolveComponentFactory(
hasAccess ?
component : // render component
ErrorForbiddenViewComponent // render Forbidden view
);
// finally use factory to create proper component
routeGuardBindComponentDirective
.viewContainerRef
.createComponent(componentFactory);
}
}
Also, this requires special directive to be defined (I am sure this can be done some other way, but I have just applied that Dynamic component example from Angular docs):
#Directive({
selector: '[route-guard-bind-component]'
})
export class RouteGuardBindComponentDirective {
constructor(public viewContainerRef: ViewContainerRef) {}
}
It isn't full answer to my own question (but its a start), so if somebody provides something better (i.e. a way to still use canActivate and ability to lazy load) I'll make sure to take that into account.
Your RouteGuard can inject whatever service you're using for modal windows, and the .canActivate() can pop the modal without redirection to inform the user without disturbing the current state of the app.
We use toastr and its angular wrapper for this, since it creates a modeless pop-up that self-dismisses after so-many seconds, no OK/Cancel buttons needed.
I've recently come across the same problem. In the end, I couldn't manage to do this using CanActivate guard, so I've implemented the authorisation logic in the component that holds the <router-outlet>.
Here is its template:
<div class="content">
<router-outlet *ngIf="(accessAllowed$ | async) else accessDenied"></router-outlet>
</div>
<ng-template #accessDenied>
<div class="message">
<mat-icon>lock</mat-icon>
<span>Access denied.</span>
</div>
</ng-template>
And its source code:
import { ActivatedRoute, ActivationStart, Router } from '#angular/router';
import { filter, switchMap, take } from 'rxjs/operators';
import { merge, Observable, of } from 'rxjs';
import { Component } from '#angular/core';
#Component({
selector: 'app-panel-content',
templateUrl: './content.component.html',
styleUrls: ['./content.component.scss'],
})
export class PanelContentComponent {
/**
* A stream of flags whether access to current route is permitted.
*/
accessAllowed$: Observable<boolean>;
constructor(
permissions: UserPermissionsProviderContract, // A service for accessing user permissions; implementation omitted
route: ActivatedRoute,
router: Router,
) {
const streams: Observable<boolean>[] = [];
/*
The main purpose of this component is to replace `<router-outlet>` with "Access denied"
message, if necessary. Such logic will be universal for all possible route components, and
doesn't require any additional components - you will always have at least one component with
`<router-outlet>`.
This component contains `<router-outlet>`, which by definition means that all possible authorisable
routes are beneath it in the hierarchy.
This implicates that we cannot listen to `route.data` observable of `ActivatedRoute`, because the route
itself in this component will always be the parent route of the one we need to process.
So the only real (the least hacky, IMO) solution to access data of child routes is to listen to
router events.
However, by the time an instance of this component is constructed, all routing events will have been
triggered. This is especially important in case user loads the page on this route.
To solve that, we can merge two streams, the first one of which will be a single access flag
for **activated route**, and the second will be a stream of flags, emitted from router
events (e.g. caused by user navigating through app).
This approach requires that the authorised route is bottom-most in the hierarchy, because otherwise the
last value emitted from the stream created from router events will be `true`.
*/
const deepestChild = this.findDeepestTreeNode(route);
const currentData = deepestChild.routeConfig.data;
// `data.authActions` is just an array of strings in my case
if (currentData &&
currentData.authActions &&
Array.isArray(currentData.authActions) &&
currentData.authActions.length > 0) {
streams.push(
// `hasPermissions(actions: strings[]): Observable<boolean>`
permissions.hasPermissions(currentData.authActions).pipe(take(1))
);
} else {
// If the route in question doesn't have any authorisation logic, simply allow access
streams.push(of(true));
}
streams.push(router.events
.pipe(
filter(e => e instanceof ActivationStart),
switchMap((event: ActivationStart) => {
const data = event.snapshot.data;
if (data.authActions &&
Array.isArray(currentData.authActions) &&
data.authActions.length > 0) {
return permissions.hasPermissions(data.authActions);
}
return of(true);
}),
));
this.accessAllowed$ = merge(...streams);
}
/**
* Returns the deepest node in a tree with specified root node, or the first
* encountered node if there are several on the lowest level.
*
* #param root The root node.
*/
findDeepestTreeNode<T extends TreeNodeLike>(root: T): T {
const findDeepest = (node: T, level = 1): [number, T] => {
if (node.children && node.children.length > 0) {
const found = node.children.map(child => findDeepest(child as T, level + 1));
found.sort((a, b) => a[0] - b[0]);
return found[0];
} else {
return [level, node];
}
};
return findDeepest(root)[1];
}
}
interface TreeNodeLike {
children?: TreeNodeLike[];
}
I've explained the approach in comments in the source code, but in short: access authorisation data in route.data using router events, and replace <router-outlet> with an error message if access is denied.
This question is a follow-up question for my original question Linking directly to both parent+child views/controllers from the main navigation menu
The accepted answer is great, but later when I added a 2nd "childRoute" dynamically, I noticed a problem. In order to build my navigation dynamically I had to add the multiple routes with the same "route" attribute. (see app.js in the example code below). The only difference were the "title" and "settings" attributes.
configureRouter(config, router){
config.title = 'Aurelia';
config.map([
{ route: 'shared-parent', moduleId: './shared-parent', settings: { childRoute: 'child-a' }, nav: true, title: 'Shared Parent - child-a' },
{ route: 'shared-parent', moduleId: './shared-parent', settings: { childRoute: 'child-b' }, nav: true, title: 'Shared Parent - child-b' },
...
]);
this.router = router;
}
The settings attribute I used in the view for doing this:
<a if.bind="row.settings.childRoute" href.bind="row.href + '/' + row.settings.childRoute">${row.title}</a>
I know it's not pretty but it does navigate to the right child route. The problem is that it's always the last of the 2 routes with duplicate "route" attributes that is marked as active.
The reason why I added the settings: {childRoute: 'child-a/b' } instead of giving them distinct "route" attributes like route: 'shared-parent/child-a' and route: 'shared-parent/child-b' was that the url would actually then match shared-parent/child-a/child-a and shared-parent/child-b/child-b since we're first linking to the shared-parent.
This live runnable gist should clearly display the problem (child-a route never activating): https://gist.run/?id=95469a9cb3a762d79da31e0b64248036
Ps.
If you have a better idea of what to call the title of this question please feel free to edit it.
So I took a stab at your problem using the EventAggregator in the Activate lifecycle hook in the child view models.
https://gist.run/?id=bfb5df5e39ac0bb73e9e1cae2d2496e2
in the child view models, I just published an event stating the child route was updated:
activate(params, routeConfig, navigationInstruction) {
let payload = navigationInstruction.parentInstruction.config.title;
payload = payload.substring(0, payload.length - 7);
this.aggregator.publish("child route updated", payload + "child-a");
}
In the app.js file, I updated the route titles, and added an activeChild property. Next, update the activeChild property when the event is captured:
constructor(aggregator) {
this.aggregator = aggregator;
this.aggregator.subscribe("child route updated", payload => {
this.activeChildRoute = payload;
console.log(this.activeChildRoute);
});
}
Finally, I updated the class expression on you list item to update based on that active child Flag:
<li repeat.for="row of router.navigation"
class="${row.title === activeChildRoute ? 'active' : ''}">
I have a problem with scroll binding. I tried to use scroll binding and event listeners without luck. Using jquery doesn't work either. Any other event I tried works as expected, but with scroll it does literally nothing. No error, no message .. What am I missing?
Edit
Ok I made a mistake and didn't put it in the app.js and app.html as mentioned in examples but one layer under so there was no scroll event to catch on the element. If I put scroll.trigger there it works.
New question:
Can scroll be triggered in children views?
Eg.: lets say you have 2 view-models on 2 routes and you want one to trigger scroll event and the other not to. Can someone point me to right direction?
Can check this ? website for detail
I found mistake causing my original problem - it is described in question.
To answer my second question I used this solution:
app.html
<div class="page-host" scroll.trigger="handleScrollEvent($event) & debounce:200">
<router-view></router-view>
</div>
app.js
export class App {
configureRouter(config, router) {
config.title = 'Title';
config.map([
{ route: ['', 'first'], name: 'first', moduleId: './first', nav: true, title: 'First route' },
{ route: ['second'], name: 'second', moduleId: './second', nav: true, title: 'Second route' }
]);
this.router = router;
}
handleScrollEvent(e) {
// if not on specified route
if(this.router.currentInstruction.config.name !== 'second') {
return;
}
// if scrolled under defined pixels
if(e.target.scrollTop > 100) {
$('body').find('#toolbar').addClass('fixed');
} else {
$('body').find('#toolbar').removeClass('fixed');
}
}
}
I don't know how clean it is but it is working. Feel free to sugest other solution.
I'm try to pass data between Vuejs views with vue-router.
//View1.vue
route: {
data: function (transition) {
transition.next({
message: "this is it!!"
});
}
}
I call next wiew with a click action button with:
//View1.vue
methods:{
showResult: function(){
this.$router.go('/View2');
}
}
but the data are not filled in the next view:
//View2.vue
<template>
<p>Message: {{ message }}</p>
</template>
Does somebody knows what's wrong with my usage of vue-router? I don't think I need to pass through services for this, right?
Working examples on jsfiddle (or jsbin, etc) are welcome :D
If View2 is a child component you can pass it using props:
//View1.vue
<view2-component :passedData='message'></view2-component>
Alternatively, I believe if you set data on the $route object from View1, since that object is shared between all vue instances, I believe it will be available application-wide.
//View1.vue
this.$router.myProps.message = message
But arguably the better way to share data is use a POJO - plain old javascript object and bind it to both views. To do this you typically need a shared state object and you can if you wish use Vuex for this although it is a little more complicated than a POJO.
I know this has already been answered, but if someone is here looking for a way to pass data to a route from a router, I use Meta Data.
Not sure if this is what the questioner meant or not but I think it is?
I personally prefer this to props just because I am more used to using it.
It allows for data to be easily passed and received without having to modify children.
Anyway here is a snippit and link!
import Vue from 'vue'
import Router from 'vue-router'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
name: 'Homepage',
meta: {
logo:{
"/imgs/Normal-Logo.png"
}
}
},
{
path: '/admin',
name: 'Admin',
meta: {
logo:{
"/imgs/Admin-Logo.png"
}
}
},
]
})
In any children who want to use vars:
<logo :src="this.$route.meta.logo"/>
Ref:
https://router.vuejs.org/guide/advanced/meta.html