Aurelia - Switching between app roots with different route configurations - aurelia

UPDATE
Could this issue have something to do with mu problem?
https://github.com/aurelia/framework/issues/400
I have an Aurelia application with two different roots, one for loggen in users, and another for anonymous users.
I have in other Aurelia apps implemented a chanaging of app root based on the approach in this answer. This works very well when the login module is an "isolated" module with no additional routes, but I'm having a hard time getting it to work now.
index.js - root for anonymous users
import {inject, useView, Aurelia} from "aurelia-framework";
import AuthService from "./services/auth-service";
#useView("app.html")
#inject(AuthService)
export class Index {
constructor(authService) {
this.auth = authService;
}
configureRouter(config, router) {
config.title = "Super Secret Project";
config.options.pushState = true;
config.map([
{ route: ["","home"], moduleId: "home", nav: true, title: "Beginscherm" },
{ route: "over", moduleId: "about", nav: true, title: "Over" },
{ route: "inloggen", moduleId: "account/login", nav: false, title: "Inloggen" }
]);
this.router = router;
}
}
ic-app.js - root for logged in users
import {useView, inject} from "aurelia-framework";
import {RequestStatusService} from "./services/request-status-service";
import AuthService from "./services/auth-service";
#useView("app.html")
#inject(RequestStatusService, AuthService)
export class App {
constructor(requestStatusService, authService) {
this.requestStatusService = requestStatusService;
this.auth = authService; // we use it to bind it to the nav-bar-top
}
configureRouter(config, router) {
config.title = "Super Secret Project";
config.options.pushState = true;
config.map([
{ route: ["", "selecteer-school"], moduleId: "ic/select-school", nav: false, title: "Selecteer School" },
{ route: "dashboard", moduleId: "ic/dashboard", nav: true, title: "Beginscherm" },
]);
this.router = router;
}
}
login code on auth-service.js
logIn(userData, rememberMe = false) {
this.requestStatusService.isRequesting = true;
return this.http
.fetch("/token", { method: "POST", body: userData })
.then((response) => response.json())
.then(response => {
if (response.access_token) {
this.setAccessToken(response.access_token, response.userName, rememberMe);
this.app.setRoot("ic-app");
}
});
}
and...
log off code in auth-service.js
logOff() {
AuthService.clearAccessToken();
this.app.setRoot("index");
}
The Problem
Setting the different app roots works as expected, the problem is that I would expect the new app root to automatically navigate to the default route of the new root, bit it tries to load the route it was on the moment setRoot(...) is called.
To illustrate with an example,
I'm on the login page. current route: /inloggen
I click the log in button. app.setRoot("ic-app") is called
New root is loaded; configureRouter in ic-app.js is called, and then...
Console error: Route not found: /inloggen
The new root tries to stay in the same /inloggen route, but I would like it to load, or navigate to, the default route for that app root.
The same happens on logging out.
How can I force the app to navigate to the default route after changing root?

I got this working great, I answered more about how to in this stackoverflow thread:
Aurelia clear route history when switching to other app using setRoot
Basically do the following
this.router.navigate('/', { replace: true, trigger: false });
this.router.reset();
this.router.deactivate();
this.aurelia.setRoot('app');

In the router for anonymous users use the mapUnknownRoutes. Like this:
configureRouter(config, router) {
config.title = "Super Secret Project";
config.options.pushState = true;
config.map([
{ route: ["","home"], moduleId: "home", nav: true, title: "Beginscherm" },
{ route: "over", moduleId: "about", nav: true, title: "Over" },
{ route: "inloggen", moduleId: "account/login", nav: false, title: "Inloggen" }
]);
config.mapUnknownRoutes(instruction => {
//check instruction.fragment
//return moduleId
return 'account/login'; //or home
});
this.router = router;
}
Do the same strategy in the other router. Now, try to logout and login again, you will see the user will be redirected to his last screen.
EDIT
Another solution is redirecting to desired route after setting the rootComponent. For instance:
logOut() {
this.aurelia.setRoot('./app')
.then((aurelia) => {
aurelia.root.viewModel.router.navigateToRoute('login');
});
}
Here is a running example https://gist.run/?id=323b64c7424f7bec9bda02fe2778f7fc

My best practice would be to simply direct them there, and this is what I do in my applications:
login(username, password) {
this.auth.login(username, password)
.then(() => {
this.aurelia.setRoot('app');
location.hash = '#/';
});
}

Related

Vuex action does not work properly in a vue mounted hook

I'm building a small e-commerce store with an admin panel for myself.
I use Firebase firestore as my backend to store all the user's data.
I have a root 'users' collection with a document for every single registered user and everything else each user has is branching out of the user doc.
Here are firestore commands i perform so you understand the structure better.
db.collection('users').doc(userId).collection('categories').doc(subCategoryId)...
db.collection('users').doc(userId).collection('subcategories').doc(subCategoryId)...
I use Vuex so every time i need to change something on my firestore (update a product category, remove a category etc.), i dispatch an appropriate action.
The first thing any of those actions does is to go ahead and dispatch another action from auth.js that gets the userId.
The problem is that if the action in question should run in a mounted() lifecycle hook, then it fails to grab the userId.
In EditCategory.vue updateCategory action works perfectly well because SubmitHandler() is triggered on click event but in Categories.vue the fetchCategories does not work and spit out an error:
[Vue warn]: Error in mounted hook (Promise/async): "FirebaseError: [code=invalid-argument]: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: null"
Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: null
Which, as far as i understand it, basically tells me that fetchCategories() action's firestore query could not be performed because the userId was not recieved.
After two days of moving stuff around i noticed that errors only occur if i refresh the page. If i switch to other tab and back on without refreshing, then fetchCategories() from Categories.vue mounted() hook works. Placing the code in to created() hook gives the same result.
I think that there is some fundamental thing i am missing about asynchronous code and lifecycle hooks.
Categories.vue component
<template>
<div class="category-main">
<section>
<div class="section-cols" v-if="!loading">
<EditCategory
v-on:updatedCategory="updatedCategory"
v-bind:categories="categories"
v-bind:key="categories.length + updateCount"
/>
</div>
</section>
</div>
</template>
<script>
import EditCategory from '#/components/admin/EditCategory.vue'
export default {
name: 'AdminCategories',
components: {
EditCategory,
},
data: () => ({
updateCount: 0,
loading: true,
categories: [],
}),
async mounted() {
this.categories = await this.$store.dispatch('fetchCategories');// FAILS!
this.loading = false;
},
methods: {
addNewCategory(category) {
this.categories.push(category);
},
updatedCategory(category) {
const catIndex = this.categories.findIndex(c => c.id === category.id);
this.categories[catIndex].title = category.title;
this.categories[catIndex].path = category.path;
this.updateCount++;
}
}
}
</script>
category.js store file
import firebase, { firestore } from "firebase/app";
import db from '../../fb';
export default {
actions: {
async getUserId() {
const user = firebase.auth().currentUser;
return user ? user.uid : null;
},
export default {
state: {
test: 10,
categories: [],
subCategories: [],
currentCategory: '',
},
mutations: {
setCategories(state, payload){
state.categories = payload;
},
},
actions: {
async fetchCategories({commit, dispatch}) {
try {
const userId = await dispatch('getUserId');
const categoryArr = [];
await db.collection('users').doc(userId).collection('categories').get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
categoryArr.push({ id: doc.id, ...doc.data() })
});
})
commit('setCategories', categoryArr);
return categoryArr;
} catch (err) { throw err; }
},
async updateCategory({commit, dispatch}, {title, path, id}) {
try {
const userId = await dispatch('getUserId');
console.log('[category.js] updateCategory', userId);
await db.collection('users').doc(userId).collection('categories').doc(id).update({
title,
path
})
commit('rememberCurrentCategory', id);
return;
} catch (err) {throw err;}
}
},
}
auth.js store file
import firebase, { firestore } from "firebase/app";
import db from '../../fb';
export default {
actions: {
...async login(), async register(), async logout()
async getUserId() {
const user = firebase.auth().currentUser;
return user ? user.uid : null;
},
},
}
index.js store file
import Vue from 'vue'
import Vuex from 'vuex'
import auth from './auth'
import products from './products'
import info from './info'
import category from './category'
Vue.use(Vuex)
export default new Vuex.Store({
modules: {
auth, products, info, category,
}
})
EditCategory.vue
export default {
name: 'EditCategory',
data: () => ({
select: null,
title: '',
path: '',
current: null
}),
props: {
categories: {
type: Array,
required: true
}
},
methods: {
async submitHandler() {
if (this.$v.invalid){
this.$v.$touch()
return;
}
try {
const categoryData = {
id : this.current,
title: this.title,
path: this.path
};
await this.$store.dispatch('updateCategory', categoryData);// WORKS!
this.$emit('updatedCategory', categoryData);
} catch (err) { throw err; }
},
},
//takes current category id from store getter
computed: {
categoryFromState() {
return this.$store.state.currentCategory;
}
},
created() {
console.log('[EditCategory.vue'], currentCategory);
},
mounted(){
this.select = M.FormSelect.init(this.$refs.select);
M.updateTextFields();
},
destroyed() {
if (this.select && this.select.destroy) {
this.select.destroy;
}
}
}
</script>
First of all, it's just a small detail, but you don't need need to make your 'getUserId' action async, since it does not use the 'await' keyword. So can simplify this :
async getUserId() {
const user = firebase.auth().currentUser;
return user ? user.uid : null;
}
const userId = await dispatch('getUserId')
into this :
getUserId() {
const user = firebase.auth().currentUser;
return user ? user.uid : null;
}
const userId = dispatch('getUserId')
Coming back to your id that seems to be undefined, the problem here is that your 'mounted' event is probably triggered before the 'login' can be completed.
How to solve this case ? Actually, there are a lot of different ways to approch this. What I suggest in your case is to use a middleware (or a 'route guard'). This guard can make you are verified user before accessing some routes (and eventually restrict the access or redirect depending on the user privileges). In this way, you can make sure that your user is defined before accessing the route.
This video is 4 years old so it is not up to date with the last versions of Firebas. But I suggest The Net Ninja tutorial about Vue Route Guards with Firebase if you want to learn more about this topic.
Accepted answer actually pointed me to the correct direction.
In my case i had to make a route guard for child routes.
router.vue
import Vue from 'vue'
import Router from 'vue-router'
import firebase from 'firebase/app';
Vue.use(Router);
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'home',
meta: {layout: 'main-layout'},
component: () => import('./views/main/Home.vue')
},
{
path: '/bouquets',
name: 'bouquets',
meta: {layout: 'main-layout'},
component: () => import('./views/main/Bouquets.vue')
},
{
path: '/sets',
name: 'sets',
meta: {layout: 'main-layout'},
component: () => import('./views/main/Sets.vue')
},
{
path: '/cart',
name: 'cart',
meta: {layout: 'main-layout'},
component: () => import('./views/main/Cart.vue')
},
{
path: '/login',
name: 'login',
meta: {layout: 'empty-layout'},
component: () => import('./views/empty/Login.vue')
},
{
path: '/register',
name: 'register',
meta: {layout: 'empty-layout'},
component: () => import('./views/empty/Register.vue')
},
{
path: '/admin',
name: 'admin',
meta: {layout: 'admin-layout', auth: true},
component: () => import('./views/admin/Home.vue'),
children: [
{
path: 'categories',
name: 'adminCategories',
meta: {layout: 'admin-layout', auth: true},
component: () => import('./views/admin/Categories'),
},
{
path: 'subcategories',
name: 'adminSubcategories',
meta: {layout: 'admin-layout', auth: true},
component: () => import('./views/admin/SubCategories'),
},
{
path: 'products',
name: 'adminProducts',
meta: {layout: 'admin-layout', auth: true},
component: () => import('./views/admin/Products'),
},
]
},
{
path: '/checkout',
name: 'checkout',
meta: {layout: 'main-layout'},
component: () => import('./views/main/Checkout.vue')
},
{
path: '/:subcategory',
name: 'subcategory',
meta: {layout: 'main-layout'},
props: true,
params: true,
component: () => import('./views/main/Subcategory.vue')
},
]
})
router.beforeEach((to, from, next) => {
//if currentUser exists then user is logged in
const currentUser = firebase.auth().currentUser;
//does a route has auth == true
const requireAuth = to.matched.some(record => record.meta.auth);
//if auth is required but user is not authentificated than redirect to login
if (requireAuth && !currentUser) {
// next('/login?message=login');
next('login')
} else {
next();
}
})
export default router;
category.js fetchCategories() action
async fetchCategories({commit, dispatch}) {
const userId = await dispatch('getUserId')
try {
const categoryArr = [];
await db.collection('users').doc(userId).collection('categories').get().then((querySnapshot) => {
querySnapshot.forEach((doc) => {
categoryArr.push({ id: doc.id, ...doc.data() })
});
})
commit('setCategories', categoryArr);
return categoryArr;
} catch (err) { throw err; }
},

How to create a middleware or how to manage the below route for front end and admin

How can I manage the url for front and admin panel Via Middleware in Vue.
This is the code I have written in router/index.js file:
const router = new VueRouter({ mode: 'history', routes });
router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const loggedIn = localStorage.getItem('usertoken') == null ? false : true;
const user = JSON.parse(localStorage.getItem('user'));
//this is for admin
next('/admin/login')
next('/admin/home');
//this is my front URL
next('/terms-condition');
next('/home');
next()
})
export default router;
See the below code it may helps you
/**
* middleware for authentication
*/
router.beforeEach((to, from, next) => {
// redirect to login page if not logged in and trying to access a restricted page
const loggedIn = localStorage.getItem('usertoken') == null ? false : true;
const user = JSON.parse(localStorage.getItem('user'));
if (to.meta.portal == 'admin') {
if (to.meta.auth) {
if (!loggedIn) {
next('/admin/login')
} else if (loggedIn) {
next();
}
} else {
if (!loggedIn) {
next();
} else if (loggedIn) {
if (user.role_id == '1') {
next('/admin/home');
} else {
next('/');
}
}
}
} else if (to.meta.portal == 'front') {
if (loggedIn) {
if (user.role_id == '1') {
next('/admin/home');
} else {
next('/');
}
} else if (!loggedIn) {
if (to.path == "/admin") {
next('/admin/login');
} else {
next();
}
}
}
next()
})
export default router;
And you need to create two router files one for front and other for admin:
//front route file will look like
export default [{
path: '/',
meta: { auth: false, portal: 'front' },
component: () => import('#/components/layouts/front/main.vue'),
children: [
{
path: '/',
name: 'front-home',
title: 'Dashboard',
meta: { auth: false, portal: 'front' },
}
]
}]
//admin router file will be like
export default [
{
path: 'user',
name: 'users',
title: 'Users',
meta: { auth: true, portal: 'admin' },
component: () => import('#/components/templates/admin/user'),
}
]
Main difference is the portal that defines which portal will access by the respective route.Without portal inside meta it won't work.
The way you have implemented is correct
Once the user is successfully logged in , use if else condition to redirect to admin panel, also use Navigation guards given in vue-router
https://router.vuejs.org/guide/advanced/navigation-guards.html#per-route-guard
This help to prevent the other user to use this url directly

vue-router does not stay on same page after a page refresh

I have two routes on vue-router, /user/:uid and /itinerary/:id. The first route is shown after the user logs in and from there they select the itinerary they want to view which would bring them to the next route.
While on /itinerary/:id, if I do a page refresh/reload/f5 I see the browser address bar back on /user/:uid. In the vue-devtools, it just resets to showing /user/:uid (not a case of some router.push() or whatever). Why is this happening and how can I keep the user on /itinerary/:id even if they refresh the page?
const routes = [
{ path: '/', component: LoginPanel },
{
path: '/user/:uid',
component: ItineraryList,
beforeEnter: authGuard,
},
{
path: '/itinerary/:itinerary_id',
name: 'itineraryView',
component: ItineraryBuilder,
beforeEnter: authGuard,
},
];
function authGuard(to, from, next) {
// retrieved from localStorage
if (idToken && userEmail) {
next();
} else {
next('/');
window.alert('Please login first');
}
}
Edit: Added relevant code from ItineraryBuilder
beforeRouteLeave(to, from, next) {
if (this.confirmed) { // set on data() property
next();
} else {
this.$modal.show('dialog', {
title: 'Confirm save new changes?',
text: '',
buttons: [{
title: 'Yes',
handler: () => {
let itinerary_id = this.$route.params.itinerary_id
let queries = [];
this.itinerary.forEach(item => {
console.log(item);
let board_id = item.doc_id;
queries.push(db.collection('itineraries').doc(board_id).set(item, { merge: true }));
});
Promise.all(queries).then(() => {
// this.$store.commit('UPDATE_ITINERARY', this.itinerary);
this.$modal.hide('dialog');
this.confirmed = true
eventBus.$emit('itinerary_saved', true);
next();
})
}
},
{
title: 'No',
handler: () => {
this.$modal.hide('dialog');
next(false);
}
}]
});
}
}
I didn't notice it at first, but could it be related? I'm using this.confirm as a flag to handle if there are any changes made by the user, I'll emit a change event and set the flag to false - meaning there are unsaved changes so the current state is unconfirmed.

Aurelia Router: Good pattern for authorizing child routes

I'd like to authorize routes on a child router, but I'm not super happy with the current solution that I have. My current routes look like the following:
/
/account
/account/signin
/account/signout
To implement this, I've defined two routers - a root router and a child router for the account pages. To authorize the routes on my account child router, I added the following pipeline step to my root router config.
config.addAuthorizeStep({
run: (instruction: NavigationInstruction, next: Next): Promise<any> => {
if (!this.auth.isAuthenticated && (instruction.fragment === '/account' || instruction.fragment === '/account/profile')) {
return next.cancel(new Redirect('account/signin'))
}
if (this.auth.isAuthenticated && instruction.fragment === '/account/signin') {
return next.cancel(new Redirect('account'))
}
if (this.auth.isAuthenticated && instruction.fragment === '/account/signup') {
return next.cancel(new Redirect('account'))
}
return next();
}
})
It works, but I feel like there has to be a better way... I'd really like to accomplish the following:
Move the authorize logic to the account child router.
Use route names instead of fragments as it seems more robust
You can add auth property to route config and check for that one
http://aurelia.io/docs/routing/configuration#pipelines
import {Redirect} from 'aurelia-router';
export class App {
configureRouter(config) {
config.addAuthorizeStep(AuthorizeStep);
config.map([
{ route: ['', 'home'], name: 'home', moduleId: 'home/index' },
{ route: 'users', name: 'users', moduleId: 'users/index', settings: { auth: true } },
{ route: 'users/:id/detail', name: 'userDetail', moduleId: 'users/detail', settings: { auth: true } }
]);
}
}
class AuthorizeStep {
run(navigationInstruction, next) {
if (navigationInstruction.getAllInstructions().some(i => i.config.settings.auth)) {
var isLoggedIn = // insert magic here;
if (!isLoggedIn) {
return next.cancel(new Redirect('login'));
}
}
return next();
}
}

Aurelia router not working when resetting root

I want to use aurelia-auth in my app and also have a login page that is completely separate from the rest of the app. I have been following the tutorial at this link: https://auth0.com/blog/2015/08/05/creating-your-first-aurelia-app-from-authentication-to-calling-an-api/
The problem I am having is after I successfully login and attempt to route to the app, none of the routes are found. I get route not found regardless of what I put in for the login redirect url.
Here is my code:
app.js:
...
import { Router } from 'aurelia-router';
import { AppRouterConfig } from './router-config';
import { FetchConfig } from 'aurelia-auth';
...
#inject(..., Router, AppRouterConfig, FetchConfig)
export class App {
constructor(router, appRouterConfig, FetchConfig) {
this.router = router;
this.appRouterConfig = appRouterConfig;
this.fetchConfig = fetchConfig;
...
}
activate() {
this.fetchConfig.configure();
this.appRouterConfig.configure();
}
...
}
login.js:
import { AuthService } from 'aurelia-auth';
import { Aurelia } from 'aurelia-framework';
...
#inject(..., Aurelia, AuthService)
export class LoginScreen {
constructor(..., aurelia, authService) {
this.aurelia = aurelia;
this.authService = authService;
...
}
login() {
return this.authService.login(this.username, this.password)
.then(response => {
console.log("Login response: " + response);
this.aurelia.setRoot('app');
})
.catch(error => {
this.loginError = error.response;
alert('login error = ' + error.response);
});
}
...
}
main.js:
import config from './auth-config';
import { AuthService } from 'aurelia-auth';
import { Aurelia } from 'aurelia-framework';
...
export function configure(aurelia) {
aurelia.use
.defaultBindingLanguage()
.defaultResources()
.developmentLogging()
.router()
.history()
.eventAggregator()
...
.plugin('aurelia-auth', (baseConfig) => {
baseConfig.configure(config);
});
let authService = aurelia.container.get(AuthService);
aurelia.start()
.then(a => {
if (authService.isAuthenticated()) {
a.setRoot('app');
} else {
a.setRoot('login');
}
});
}
auth-config.js:
var config = {
baseUrl: 'http://localhost:3001',
loginUrl: 'sessions/create',
tokenName: 'id_token',
//loginRedirect: '#/home' //looks like aurelia-auth defaults to #/ which is fine for me
}
export default config;
router-config.js:
import { AuthorizeStep } from 'aurelia-auth';
import { inject } from 'aurelia-framework';
import { Router } from 'aurelia-router';
#inject(Router)
export class AppRouterConfig {
constructor(router) {
this.router = router;
}
configure() {
console.log('about to configure router');
var appRouterConfig = function (config) {
config.title = 'My App';
config.addPipelineStep('authorize', AuthorizeStep);
config.map([
{
route: ['', 'home'],
name: 'home',
moduleId: '.components/home/home',
nav: true,
title: 'Home',
auth: true
},
{
route: ['employees'],
name: 'employees',
moduleId: './components/employees/employees',
nav: true,
title: 'Employees',
auth: true
}
]);
this.router.configure(appRouterConfig);
}
};
}
When loading the app, it successfully goes to login page and I'm able to successfully login and it tries to redirect, but I get this error in the console:
ERROR [app-router] Error: Route not found: /
at AppRouter._createNavigationInstruction (http://127.0.0.1:8080/jspm_packages/npm/aurelia-router#1.0.0-rc.1.0.1/aurelia-router.js:1039:29)
at AppRouter.loadUrl (http://127.0.0.1:8080/jspm_packages/npm/aurelia-router#1.0.0-rc.1.0.1/aurelia-router.js:1634:19)
at BrowserHistory._loadUrl (http://127.0.0.1:8080/jspm_packages/npm/aurelia-history-browser#1.0.0-rc.1.0.0/aurelia-history-browser.js:301:55)
at BrowserHistory.activate (http://127.0.0.1:8080/jspm_packages/npm/aurelia-history-browser#1.0.0-rc.1.0.0/aurelia-history-browser.js:200:21)
at AppRouter.activate (http://127.0.0.1:8080/jspm_packages/npm/aurelia-router#1.0.0-rc.1.0.1/aurelia-router.js:1689:20)
at eval (http://127.0.0.1:8080/jspm_packages/npm/aurelia-router#1.0.0-rc.1.0.1/aurelia-router.js:1670:21)
at AppRouter.registerViewPort (http://127.0.0.1:8080/jspm_packages/npm/aurelia-router#1.0.0-rc.1.0.1/aurelia-router.js:1672:10)
at new RouterView (http://127.0.0.1:8080/jspm_packages/npm/aurelia-templating-router#1.0.0-rc.1.0.1/router-view.js:112:19)
at Object.invokeWithDynamicDependencies (http://127.0.0.1:8080/jspm_packages/npm/aurelia-dependency-injection#1.0.0-rc.1.0.1/aurelia-dependency-injection.js:329:20)
at InvocationHandler.invoke (http://127.0.0.1:8080/jspm_packages/npm/aurelia-dependency-injection#1.0.0-rc.1.0.1/aurelia-dependency-injection.js:311:168)error # aurelia-logging-console.js:54log # aurelia-logging.js:37error # aurelia-logging.js:70(anonymous function) # aurelia-router.js:1637
aurelia-logging-console.js:54 ERROR [app-router] Router navigation failed, and no previous location could be restored.
I'm googling around quite a bit for answers to this, but having difficulty finding good answers. Anybody have any ideas? Any help is appreciated!
I think the problem is that you are configuring the router inside activate() method. In my opinion, there is no need to do this.
You can navigate to a route after resetting the root component:
this.aurelia.setRoot('./login')
.then((aurelia) => {
aurelia.root.viewModel.router.navigateToRoute('someLoginRoute');
});
You can also use the mapUnknownRoutes, which is very useful:
configureRouter(config, router) {
config.title = "Super Secret Project";
config.map([
{ route: [ '', 'screen-1'], moduleId: "./screen-1", nav: true, title: "Beginscherm" },
{ route: 'screen-2', name:'screen-2', moduleId: "./screen-2", nav: true, title: "Beginscherm" }
]);
config.mapUnknownRoutes(instruction => {
return './screen-1';
});
this.router = router;
}
See this example https://gist.run/?id=00b8b3745a480fb04184e8440e8be8c5. Pay attention at login/logout functions.
I hope this helps!
U can solve this by reloading or refreshing the app.
after setting app
ie.
a.setRoot('app');
location.reload();