Can't load component though URL changed - vuejs2

I have this route which loads a component and makes some validations
{
path: "/sso/:id/:atex/:entityId/:ip",
name: "SSO",
component: () => import("./views/account/sso"),
meta: {
beforeResolve(to, routeFrom, next) {
if (localStorage.getItem('userDetails') || localStorage.getItem('user')) {
next({ path: "/" + to.params.entityId })
}
next();
}
},
}
In the component and if user is valid
this.$router.replace("/" + this.$route.params.entityId);
And here's the route of /
{
path: "/:entityId?",
name: "Companies",
meta: {
authRequired: true,
beforeResolve(routeTo, routeFrom, next) {
next();
},
},
component: () => import("./views/dashboards/CompaniesGrid"),
},
And in the component I search for a table cell that contains the entityId value and click it to naرigate to /dashboard
var len = document.querySelectorAll('td').length;
for (var i=0; i < len; i++){
if ( document.querySelectorAll('td')[i].innerText == (this.$route.params.entityId).toUpperCase()){
document.querySelectorAll('td')[i].setAttribute('id', this.$route.params.entityId);
document.getElementById(this.$route.params.entityId).click();
}
}
The URL changes successfully but it doesn't really navigate. The component of dashboard isn't loaded and when I click it manually I get
Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/dashboard".
How to solve this issue and why did it happen?

Related

Navigation guard for dynamic route

I have navigation guards to prevent visitors from viewing protected pages without being logged in. One of the pages I want them to see without login is a dynamic route e.g. example.com/dynamic_part. Below is my vuejs code:
router.beforeEach((to, from, next) => {
let token = window.sessionStorage.getItem("local_token");
let whitelist = [
"/",
"/register",
"/login",
"/dynamic_part/",
];
below works but it doesn't allow for the dynamic route "/dynamic_part/"
if (whitelist.includes(to.path)) {
below works for the dynamic route but breaks other route guards i.e. can't move to Products after logging in. I get this error: Error: Redirected when going from "/login" to "/Products" via a navigation guard.
whitelist.some(item => console.log(to.path.includes(item), item))
if (whitelist.some(item => to.path.includes(item))) {
The rest of the navigation guard:
if (token) {
next({
name: "Products",
});
} else {
next();
}
} else {
if (token) {
next();
} else {
next({
name: "Login",
});
}
}
});
What am I doing wrong and how can get all urls to work?
The problem here is all routes will match to.path.includes("/").
You need to separate the routes you want to match fully, with the ones you match with contains (you might want startsWith()?).
const whitelist = [
"/",
"/register",
"/login",
];
const dynamicWhitelist = [
"/dynamic_part/",
];
if (whitelist.includes(to.path) || dynamicWhitelist.some(item => to.path.includes(item))) {
/// etc
}
The more 'Vue-router-like' way of doing this is defining a meta object in your routes and testing against those.
//routes:
const routes = [
{
path: '/login',
component: Login,
meta: { allowAnon: true }
}
...
router.beforeEach((to, from, next) => {
let token = window.sessionStorage.getItem("local_token");
if(to.meta.allowAnon) {
//etc
See the docs here for more details.

Vue router lazy loading does not work in Vite (Error: Unknown variable dynamic import)

I've built the below code in Vue router and it works perfect in Vue-CLI.
import store from "./../../store/index.js";
function getView(view) {
return () => import(`#/views/settings/${view}.vue`);
}
const routes = [
{
path: "/myProfile",
name: "MyProfile",
component: getView("MyProfile"),
beforeEnter: (to, from, next) => {
document.title = "Profile - " + store.getters.getAppName;
if (store.getters["userStore/authenticated"]) {
next();
} else {
next({ name: "Home" });
}
},
}
]
export default routes;
Now I am replacing Vue-CLI with Vite and it gives the below error.
TypeError: Failed to resolve module specifier '#/views/settings/MyProfile.vue'
When I remove the getView("MyProfile") function and directly use import as below, it works.
const routes = [
{
path: "/myProfile",
name: "MyProfile",
component: () => import('#/views/settings/MyProfile.vue'),
beforeEnter: (to, from, next) => {
document.title = "Profile - " + store.getters.getAppName;
if (store.getters["userStore/authenticated"]) {
next();
} else {
next({ name: "Home" });
}
},
}
]
Can someone please, explain why?
Can someone please, explain why?
This is due to Rollup Limitations. All imports must start relative to the importing file and import should not start with a variable.
So to get the GetView() function working, you have to replace the alias (#/) with relative or absolute path ../views or /src/views :
function getView(view) {
return () => import(`../views/settings/${view}.vue`);
}
Why it is working when you remove the getView() and you write directly the import directive ?
If you set a literal string, the alias is resolved (it end up to a relative or absolute path, respecting rollup requirement).
After trying lots of options, I finally found this solution.
import store from "./../../store/index.js";
async function getView(view) {
const comps = import.meta.glob("../views/**/*.vue");
const match = comps[`../views/${view}.vue`];
//For TS: const match: () => Promise<any> = comps[`../views/${view}.vue`];
return (await match()).default;
}
const routes = [
{
path: "/myProfile",
name: "MyProfile",
component: () => getView("settings/MyProfile"),
beforeEnter: (to, from, next) => {
document.title = "Profile - " + store.getters.getAppName;
if (store.getters["userStore/authenticated"]) {
next();
} else {
next({ name: "Home" });
}
},
}
]
export default routes;
Hope, this will solve the problem. (This works for any route.)
A little bit late but this should be the answer to your question, dynamic import in different bundlers will definitely have different behavior
We have Vite's case covered in the official documentation here:
https://router.vuejs.org/guide/advanced/lazy-loading.html#with-vite
Hope that will help :)

VueJS3: Passing data from route definition

Using Vue3. I want to set roles allowed in the route definition, then use that value in beforeRouteEnter. Something like:
{
path: "secure/page",
name: "SecurePage",
component: SecurePage,
params: {role: admin},
}
Then
const guard = {
template: `...`,
beforeRouteEnter (to, from, next) {
next(vm => {
if( 'admin' === vm.$route.params.role) {
}
})
},
}
As it doesn't work. Is it possible in any way ?
You cannot access to the router params definition from the view as you try. You need something like this:
{
path: "secure/page",
name: "SecurePage",
component: SecurePage,
meta: { role: 'admin' }
}
Then in the view instead of vm.$route.params.role use vm.$route.meta.role:
beforeRouteEnter(to, from, next) {
next(vm => {
if ('admin' === vm.$route.meta.role) {
}
})
}
Just so you know, the beforeRouteEnter guard does NOT have access to this, because the guard is called before the navigation is confirmed, thus the new entering component has not even been created yet.
More about meta fields you can find here:
https://router.vuejs.org/guide/advanced/meta.html

How to fix NavigationDuplicated error in Internet Explorer?

I am developing a "Vue" application that consists of a form to make a purchase.
In all the browsers it makes me the complete cycle without any problem, managing to make the "post" at the end of the form.
On the other hand, when I try to do the flow in Internet Explorer, after filling in the last step of the form, it redirects to the next page but does not load the component, returning the error "Uncaught (in promise) NavigationDuplicated: Avoided redundant navigation to current location: "/devis/resume".
this is my router
import Vue from 'vue'
import Router from 'vue-router'
import Devis from '../components/devis/devis.vue'
import Animal from '../components/devis/animal.vue'
import Create from '../components/devis/create.vue'
import Resume from '../components/devis/resume.vue'
import Service from '../components/devis/service.vue'
import Geo from '../components/extern/geolocalisation.vue'
import Message from '../components/extern/servicenotavailable.vue'
Vue.use(Router)
export default new Router({
routes: [
{
path: '/',
component: Devis, children: [
{
path: '/',
component: Animal
}
]
},
{
path: '/devis',
component: Devis, children: [
{
path: '/',
component: Animal
},
{
path: 'create',
component: Create
},
{
path: 'resume',
component: Resume
},
{
path: 'service',
component: Service
}
]
},
{
path: '/geo',
component: Geo
},
{
path: '/message',
component: Message
}
],
mode: 'history',
scrollBehavior() {
document.getElementById('app').scrollIntoView();
}
})
and here is the point at which I redirect from create component to the resume component
checkForm() {
if (
this.ownerFormInfo.civility !== "" &&
this.ownerFormInfo.firstName !== "" &&
this.ownerFormInfo.lastName !== "" &&
this.ownerFormInfo.adresse1 !== "" &&
this.ownerFormInfo.city !== "" &&
(this.ownerFormInfo.postalCode === "" ||
(this.ownerFormInfo.postalCode !== "" &&
this.validatePostalCode(this.ownerFormInfo.postalCode))) &&
(this.ownerFormInfo.phone === "" ||
(this.ownerFormInfo.phone !== "" &&
this.validatePhone(this.ownerFormInfo.phone))) &&
(this.ownerFormInfo.email === "" ||
(this.ownerFormInfo.email !== "" &&
this.validateEmail(this.ownerFormInfo.email)))
) {
this.onSubmit();
}
},
onSubmit() {
const formData = {
civility: this.ownerFormInfo.civility,
firstName: this.ownerFormInfo.firstName,
lastName: this.ownerFormInfo.lastName,
adresse1: this.ownerFormInfo.adresse1,
adresse2: this.ownerFormInfo.adresse2,
adresse3: this.ownerFormInfo.adresse3,
city: this.ownerFormInfo.city,
postalCode: this.ownerFormInfo.postalCode,
phone: this.ownerFormInfo.phone.indexOf('0') == 0 ? this.ownerFormInfo.phone.replace('0', '+33') : this.ownerFormInfo.phone,
email: this.ownerFormInfo.email
};
const owner = {
ownerCivility: formData.civility,
ownerLastname: formData.lastName,
ownerFirstname: formData.firstName,
ownerAddressFirstLine: formData.adresse1,
ownerAddressSecondLine: formData.adresse2,
ownerAddressThirdLine: formData.adresse3,
ownerPostalCode: formData.postalCode,
ownerCity: formData.city,
ownerPhone: formData.phone,
ownerEmail: formData.email,
country: "FR"
};
this.$store.dispatch("formOwnerStepInfo", formData);
const token = localStorage.getItem("token");
let config = {
headers: {
Authorization: "Bearer " + token
}
};
globalAxios
.post("/api/fr/estimations", owner, config)
.then(res => {
if (res.data.functional_id) {
this.$store.dispatch("setFunctionalId", res.data.functional_id);
}
})
.catch(error => console.log(error));
this.navigateToResume();
},
navigateToResume() {
this.$store.dispatch("setStep", this.step + 1);
this.$router.push("/devis/resume");
},
How can it be that in the rest of the browsers it works correctly?
What am I doing wrong?
I've been looking for information but I can't find a way to fix the error or reference it as being due to Internet Explorer.
Greetings and thank you all for your time and help in advance
I found several threads, thanks to the help of Yu Zhou in comments, referring to similar problems caused by the vue-router version. All of them suggest using the vue-router below version 3.0.
In my case I first lowered it to 2.8 and there was no difference , but then I lowered it to 2.6 and the problem was solved.

Render different view dynamically in Aurelia

Is there any way in aurelia I can render different view dynamically.
async Activate(booking) {
//booking: is the route param
const hasRecord = await this.service.RecordExists(booking);
if (hasRecord) {
map(booking,form);
}
return {
//Render different template
}
}
You should try to tackle this issue in another way. Why would you want to navigate to a ViewModel and trigger its creation, just in order to not use it and load another ViewModel? Seems inefficient at best right?
Aurelia exposes pipelines on the router, you should do this check there and redirect accordingly. Look at the PreActivate step here, you could write something like this (pseudo code):
configureRouter(config, router) {
function step() {
return step.run;
}
step.run = async (navigationInstruction, next) => {
if(await this.service.RecordExists(navigationInstruction.queryParams...)
{
return next()
} else {
next.cancel(new Redirect('your other page'))
}
};
config.addPreActivateStep(step)
config.map([
{ route: ['', 'home'], name: 'home', moduleId: 'home/index' },
{ route: 'users', name: 'users', moduleId: 'users/index', nav: true },
{ route: 'users/:id/detail', name: 'userDetail', moduleId: 'users/detail' },
{ route: 'files/*path', name: 'files', moduleId: 'files/index', href:'#files', nav: true }
]);
}
EDIT
You can have cases where you don't want a redirect, for example you have users wanting to bookmark baseurl/businessobject/id, and the url is navigatable before the object actually exists
Then you can use the getViewStrategy() function on your ViewModel:
getViewStrategy(){
if(this.businessObj){
return 'existingObjectView.html';
} else {
return 'nonExisting.html';
}
}