Vue Router doesn't seem to like function calls - javascript

In my router/index.js, I am trying to lazy-load the route. If I hard-code the string it is working but if I use a function (as shown) call to get that file path as a string value, it shows me an error in the console - Error: Cannot find module '../views/Login/Login.vue'. I'm using Vue 2.6.11 and Vue-router 3.5.1.
Where am I going wrong?
import Vue from 'vue'
import VueRouter from 'vue-router'
Vue.use(VueRouter)
// const filePath = (filename, foldername) => '../views/' + foldername + "/" + filename + '.vue'
const routes = [
{
path: '/',
name: 'Login',
// THIS WORKS
component: () => import('../views/Login/Login.vue')
// THIS DOES NOT, EVEN THOUGH IT IS GETTING THE EXACT SAME VALUE
// component: () => import(filePath('Login', 'Login'))
},
]
const router = new VueRouter({
routes
})
export default router

If you want to lazy-load a route, it has to be statically analyzable, like this:
component: () => import('../views/Login/Login.vue')
In your other attempt:
component: () => import(filePath('Login', 'Login'))
The import is not statically analyzable by the build tool (probably webpack). The build tool is in charge of identifying which code is being used and which code is not (that's called tree-shaking).
As it can't resolve the actual import (it would be possible only at runtime since filePath could return anything), it considers that the vue file is never referred to, and should be removed from the build.
If your actual build tool is webpack, you have to follow webpack's instructions described here: https://webpack.js.org/api/module-methods/#dynamic-expressions-in-import which states that is possible to have dynamic imports when they have a statically analyzable pattern (e.g: template literals)

You should let webpack to know in which root folder it will load the component files, so you should move ../views/ to the import parameter instead of the returned value of your function :
const filePath = (filename, foldername) => foldername + "/" + filename + '.vue'
const routes = [
{
path: '/',
name: 'Login',
component: () => import('.../views/'+filePath('Login', 'Login'))
},
]

Related

Cypress: import modules via alias

in my project i am using cypress with plain javascript. i am facing the challenge of importing the modules (page objects) via aliases instead of spaghetti code like ../../../../folder/page.js.
I don't use typescript or react.js and don't have a src folder/directory.
my tests run locally in the browser or via a docker image (pipeline).
I would like to transform from this:
import { LoginPage } from "../../pages/loginPage.js";
to something like this:
import { LoginPage } from "#Pages/loginPage.js";
but I always get an error:
Error: Webpack Compilation Error
./cypress/e2e/accountOverview/accountOverviewPageTest.spec.js
Module not found: Error: Can't resolve 'Pages/loginPage.js' in 'C:\Users\User\automated_frontend_tests\automated_frontend_tests\cypress\e2e\accountOverview'
resolve 'Pages/loginPage.js' in 'C:\Users\User\automated_frontend_tests\automated_frontend_tests\cypress\e2e\accountOverview'
Parsed request is a module
using description file: C:\Users\User\automated_frontend_tests\automated_frontend_tests\package.json (relative path: ./cypress/e2e/accountOverview)
Field 'browser' doesn't contain a valid alias configuration
Looked for and couldn't find the file at the following paths:
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\cypress\e2e\accountOverview\node_modules]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\cypress\e2e\node_modules]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\cypress\node_modules]
[C:\Users\node_modules]
[C:\node_modules]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\node_modules\Pages\loginPage.js]
[C:\Users\User\automated_frontend_tests\node_modules\Pages\loginPage.js]
[C:\Users\User\node_modules\Pages\loginPage.js]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\node_modules\Pages\loginPage.js.js]
[C:\Users\User\automated_frontend_tests\node_modules\Pages\loginPage.js.js]
[C:\Users\User\node_modules\Pages\loginPage.js.js]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\node_modules\Pages\loginPage.js.json]
[C:\Users\User\automated_frontend_tests\node_modules\Pages\loginPage.js.json]
[C:\Users\User\node_modules\Pages\loginPage.js.json]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\node_modules\Pages\loginPage.js.jsx]
[C:\Users\User\automated_frontend_tests\node_modules\Pages\loginPage.js.jsx]
[C:\Users\User\node_modules\Pages\loginPage.js.jsx]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\node_modules\Pages\loginPage.js.mjs]
[C:\Users\User\automated_frontend_tests\node_modules\Pages\loginPage.js.mjs]
[C:\Users\User\node_modules\Pages\loginPage.js.mjs]
[C:\Users\User\automated_frontend_tests\automated_frontend_tests\node_modules\Pages\loginPage.js.coffee]
[C:\Users\User\automated_frontend_tests\node_modules\Pages\loginPage.js.coffee]
[C:\Users\User\node_modules\Pages\loginPage.js.coffee]
# ./cypress/e2e/accountOverview/accountOverviewPageTest.spec.js 5:17-46
I have tried several solutions, including:
//webpack.config.js
module.exports = {
resolve: {
alias: {
"#pages": path.resolve(__dirname, "cypress/pages/*"),
},
},
};
//testspec file
import { LoginPage } from "#pages/loginPage.js";
const loginPage = new LoginPage();
#Uzair Khan:
I tried your solution, but it still didn't work. The error message remains the same. It seems that the IDE does not search in the correct folder, but only in ...\node_modules\#page\loginPage.js which makes no sense.
If I enter const loginPage = new LoginPage(), the module LoginPage() cannot be found by the IDE either. Something is wrong with the solution. Do I still have to install any packages via NPM?
In your webpack.config.js file add resolve.alias which you want to make alias. It looks like something this below:
resolve: {
alias: {
'#page': path.resolve(__dirname, '{path you want to make alias}')
}
}
Since you are using cypress, you have to update the resolve path in cypress.config.js. Here is mine cypress.config.js
import { defineConfig } from 'cypress'
import webpack from '#cypress/webpack-preprocessor'
import preprocessor from '#badeball/cypress-cucumber-preprocessor'
import path from 'path'
export async function setupNodeEvents (on, config) {
// This is required for the preprocessor to be able to generate JSON reports after each run, and more,
await preprocessor.addCucumberPreprocessorPlugin(on, config)
on(
'file:preprocessor',
webpack({
webpackOptions: {
resolve: {
extensions: ['.ts', '.js', '.mjs'],
alias: {
'#page': path.resolve('cypress/support/pages/')
}
},
module: {
rules: [
{
test: /\.feature$/,
use: [
{
loader: '#badeball/cypress-cucumber-preprocessor/webpack',
options: config
}
]
}
]
}
}
})
)
// Make sure to return the config object as it might have been modified by the plugin.
return config
}
And import in other file via that alias you set in cypress.config.js. Here is mine for example:
import page from '#page/visit.js'
const visit = new page()
When('I visit duckduckgo.com', () => {
visit.page()
})
I think both answers are nearly there, this is what I have for src files:
const webpack = require('#cypress/webpack-preprocessor')
...
module.exports = defineConfig({
...
e2e: {
setupNodeEvents(on, config) {
...
// #src alias
const options = {
webpackOptions: {
resolve: {
alias: {
'#src': path.resolve(__dirname, './src')
},
},
},
watchOptions: {},
}
on('file:preprocessor', webpack(options))
...
path.resolve() resolves a relative path into an absolute one, so you need to start the 2nd param with ./ or ../.
Also, don't use wildcard * in the path, you just need a single folder that will be substituted for the alias in the import statement.
If in doubt, check the folder returned (in the terminal)
module.exports = defineConfig({
...
e2e: {
setupNodeEvents(on, config) {
const pagesFolder = path.resolve(__dirname, './cypress/pages')
console.log('pagesFolder', pagesFolder)

Best way to check what page you're on in Vue.js

I have
these simple route/URL when I am in a car details page
http://localhost:8080/car/1
I am using vue2; what is the best way to check if I am on a car page?
I normally
would have checked for the first segment of the URL, but I wasn't sure if that is the best future-proof approach.
Questions
Should I use JS to detect what page I am ?
Should I use Vue functionality to access the router object?
Why would one decide to pick one over another?
You could provide a name for your route inside the routes definition like :
{
path: '/car/{id}',
name: 'car',
component: CarView
},
then access it using this.$route.name or you could parse the this.$route.path to get the name using String object methods
Perhaps, try using: router.currentRoute.path, where router is:
import Router from "vue-router";
Vue.use(Router);
const routes = [
{ path: "/", component: Home },
{ path: "/test1", component: Test1 },
{ path: "/test2", component: Test2 }
];
const router = new Router({
routes
});
console.log('Current route: ', router.currentRoute.path);

How to setup NextJS Middleware [duplicate]

I have the middleware.js file within /myproject/pages/middleware.js:
export function middleware(request) {
console.log(1);
return NextResponse.redirect(new URL('/', request.url));
}
// See "Matching Paths" below to learn more
export const config = {
matcher: ['/test'],
};
Now what I expect is when i go to the page /test then it should redirect me to /. However nothing happens and I see my standard 404 page.
Any ideas why?
NextJs version: 12.2.2
Latest versions of NextJS requires user to have a single middleware on the root folder.
Instead of {root}/pages/_middleware.js, try {root}/middleware.js
For next 13.0.2 / 13.0.1
if you are using appDir: true ( experimental )
if you want to hit middleware:
put middleware.ts in root project:
( as the same hierarchy as "app" folder, not inside app folder... )
make sure tsconfig has include: [..., "middleware.ts"]
make empty "pages" folder. ( based on issue )
will hit every request:
import { NextResponse } from 'next/server'
import type { NextRequest } from 'next/server'
export function middleware(request: NextRequest ) {
console.log('lol!!!')
}
export const config = {
matcher: '/',
}
if your pages and middleware are not on the same level, then it won't work.
here is an example of middleware working with Cookies.
import { NextResponse } from "next/server";
export default function middleware(req){
let verify = req.cookies.get("loggedin");
let url = req.url
if(!verify && url.includes('/dashboard')){
return NextResponse.redirect("http://localhost:3000/");
}
if (verify && url === "http://localhost:3000/") {
return NextResponse.redirect("http://localhost:3000/dashboard");
}
}

Pass variable from webpack entries

I have multiple entries like in webpack docs
module.exports = {
entry: {
pageOne: './src/pageOne/index.js',
pageTwo: './src/pageTwo/index.js',
pageThree: './src/pageThree/index.js'
}
};
let's say every of these files (index.js) is the same and only thing that change is other routes import.
I would create one file to keep DRY principle but I would need to pass something to actually know which router I want to import.
For example pageOne/index.js is
import Vue from 'vue'
import axios from 'axios'
import App from '../App'
import { store } from '../store';
import VueProgressBar from 'vue-progressbar'
import Router from '../routers/router';
import interceptor from '../../config/httpInterceptor.js';
console.log('in main', window.location.href)
const routerInstance = new Router('main');
routerInstance.createRouter()
.then(router => {
interceptor();
if (!process.env.IS_WEB) Vue.use(require('vue-electron'));
Vue.http = Vue.prototype.$http = axios;
Vue.config.productionTip = false;
Vue.use(VueProgressBar, {
color: 'rgb(255, 85, 0)', failedColor: 'red', thickness: '5px'
});
new Vue({
components: { App },
router,
store,
template: '<App/>'
}).$mount('#app');
});
and in pageTwo/index.js only line that change is from
const routerInstance = new Router('main');
to
const routerInstance = new Router('second');
so it's bad to have three same js files where only one line is changing but I don't know how to refactor it so it's reusable. I would need to pass to file information about which page is being loaded for example it would be something like this
module.exports = {
entry: {
pageOne: './src/pageOne/index.js?page=main',
but now when in index.js I log
console.log('in main', window.location.href)
it is
http://localhost:9080/#/
So I'm not able to require different router basing on this. I need some other solution.
Edit
#Daniel Lizik in comment section suggested to use environment variable. He also said to use webpack config in order to set it. Considering that I would refactor my entry object to following:
module.exports = {
entry: {
pageOne: './src/index.js',
pageTwo: './src/index.js',
pageThree: './src/index.js'
}
};
I would need to configure webpack somehow to set environment variable right after webpack imported file.
Webpack probably loop over this object and import files. Right after it read this line
pageOne: './src/index.js',
I would need to set some env variable to 'pageOne' so when index.js is executed it can check this variable and import correct router.
However I have no idea how I could achieve that.
You don't need to pass the query param in the entry point configuration. You have to pass it from the browser. Then you can parse the query param and pass it to new Router(). You can use urijs node module for parsing the query param in a clean way.
import URI from "urijs";
...
const uriObj = new URI(url);
const page = uriObj.query(true)[page] || "main";
const routerInstance = new Router(page);
In your browser you enter the url with the page param like http://localhost:9080/page=second

.env Nuxt JS doesn't resolve routes in generate option correctly

I'm trying to use Nuxt JS's 2.9.2 generate object to generate dynamic pages as static files using my .env file to pull a URL, I'm having difficuility in getting it to properly link up:
nuxt.config.js
require('dotenv').config();
import pkg from './package'
import axios from 'axios'
export default {
mode: 'universal',
env: {
blog_api: process.env.BLOG_API || "http://localhost:3000/articles/blogs.json"
},
/*
** Build directory
*/
generate: {
dir: 'dist-next',
routes: function () {
return axios.get(`${process.env.blog_api}`)
.then((res) => {
return res.data.blogs.map((blog) => {
return '/posts/view/' + blog.title
})
})
}
}
}
The above code, more specifically ${process.env.blog_api}, can't seem to resolve the routes, despite it working perfectly if I replace it with my own local domain.
.env
BLOG_API="http://my-local-domain.clone/articles/blogs.json"
EDIT:
Updated code with my config, http://my-local-domain.clone/articles/blogs.json is inside of static/articles
You should use dotenv module:
https://www.npmjs.com/package/dotenv
More Info about configuration with NUXT you have here:
https://samuelcoe.com/blog/nuxt-dotenv/
You probably want to set your env property in nuxt.config.js, for example:
module.exports = {
env: {
BLOG_API: process.env.BLOG_API_URL,
},
In your component, you can now use them :
makeAsyncCall({
to: process.env.BLOG_API,
})

Categories