Extend several new paths to a single page in Nuxt - vue.js

I am struggeling with the nuxt folder/route structure for a project:
I want to achieve:
All of them should show pages/data/index.vue:
www.example.com/data
www.example.com/data/region
www.example.com/data/region/industry
And then access the parameter region or industry via the $route-class.
If I add _region folder and an _industy.vue it will show those files and I want to show and use the index.vue.

EDIT: Since region and industry are probably dynamic.
You could use this setup in your nuxt.config.js file
export default {
router: {
extendRoutes(routes, resolve) {
routes.push(
{
name: 'data-region-industry',
path: '/data/*/*',
component: resolve(__dirname, 'pages/data/index.vue'),
},
{
name: 'data-region',
path: '/data/*',
component: resolve(__dirname, 'pages/data/index.vue'),
},
)
}
}
}
With this configuration, you can go to either /data, /data/:region or /data/:region/:industry with only your index.vue file. No need to make some strange directories or file, you can keep all in one single place.
PS: the order is important. Put the most specific at top, otherwise /data/* will also catch /data/*/* and you'll never reach data-region-industry. This can be double-checked pretty quickly in the router tab of the Vue devtools.
This was taken from the official documentation: https://nuxtjs.org/docs/2.x/features/file-system-routing#extendroutes
I highly recommend giving it a read, especially if you are using Named views.
As for the URL catch, never heard of $route-class but you could make some kind of split on /, pretty doable!

you could use query in the url instead of params.
www.example.com/data
www.example.com/data/?region=x
www.example.com/data/?region=x&?industry=y
you are still able to access the query data via $route.query. if you don't want to use query you have to manually overwrite vue router of nuxt as far as I know.

Related

Custom PageLayoutComponent

To have specific layout for some pages at our project we create few custom PageLayoutComponent's. Some contfiguration example:
{
// #ts-ignore
path: null,
canActivate: [CmsPageGuard],
component: CartPageLayoutComponent,
data: {
cxRoute: 'cart',
cxContext: {
[ORDER_ENTRIES_CONTEXT]: ActiveCartOrderEntriesContextToken,
},
},
},
All work fine with storefront until you will not try to select specific page at smartedit. As result it not use our custom CartPageLayoutComponent, but will use PageLayoutComponent for rendering.
Probably this is because it's not a normal route navigation. Can somebody from spartacus team suggest how this bug can be fixed?
Probably this is because it's not a normal route navigation
I believe your Route should be recognized normally, there is nothing special in adding a custom Angular Route.
So I guess there is something special about the page or URL of Spartacus Storefront that runs in your SmartEdit.
It's hard to tell the reason of your problem without more debugging.
You said your app works as expected when run differently (locally?), but when used in SmartEdit, then there is a problem. Please identify factors that makes the run SmartEdit different from your (local?) run. And try to isolate them. Guesses from top of my head:
production vs dev mode of the build?
exact URL of the cart page?
any difference in configuration between a local version and deployed one to be used in SmartEdit?
I would also add some debugging code to better know which routes configs are available and which one is used for the current route. For debugging purposes please add the following constructor logic in your AppModule:
export class AppModule {
// on every page change, log:
// - active url
// - the Route object that was matched with the active URL
// - all the Route objects provided to Angular Router
constructor(router: Router) {
router.events.subscribe((event) => {
if (event instanceof NavigationEnd) {
console.log({
activeUrl: router.url,
activeRouteConfig:
router.routerState.snapshot.root.firstChild.routeConfig,
allRoutesConfigs: router.config,
});
}
});
}
}
The pages opened in SmartEdit have the same route of cx-preview (e.g. to open faq page in smartedit, request is https://localhost:4200/electronics-spa/en/USD/cx-preview?cmsTicketId=xxxxx. backend can get page id from cmsTicketId). If you want to change the page layout, you can consider use PageLayoutHandler. Spartacus has some PageLayoutHandlers, e.g.
{
provide: PAGE_LAYOUT_HANDLER,
useExisting: CartPageLayoutHandler,
multi: true,
},

VueJS router alias to particular param

I have a lot of articles in my app, and the URL are written like this in Vue Router: /article/:id.
I have particular articles I want to "pin" and have easier URLs. For example: /pinned-article, which should point to /article/3274 and /other-pinned-article, pointing to /article/68173.
I though about adding this to my routes, but it doesn't work:
{ path: '/article/3274', component: Article, alias: '/pinned-article' }
I thought about something else, involving another component:
{ path: '/pinned-article/:id', component: PinnedArticle }
The component PinnedArticle silently aliasing the correct article with a command like router.alias in the <script> section, but it apparently doesn't exist.
Is there a way to solve this problem? I thought I could use some answers I read here in Stackvoverflow (for examples when it comes to redirect /me to /user/:id, but it doesn't apply.
Thanks in advance :)
addRoute
You can achieve this with Dynamic Routing, which is not the same as dynamic route matching, i.e. route params.
(This solution works in both Vue 3 and Vue 2 with Vue Router >= 3.5.0)
By using the addRoute method of Vue router, you can create routes at runtime. You can either use a redirect or not, depending on whether you want the url bar to read /article/3274 or /pinned.
Redirect
If you want the url to change from /pinned to /article/3274, use redirect:
methods: {
pinRoute() {
this.$router.addRoute({
path: '/pinned',
name: 'pinned',
redirect: { name: 'article', params: { id: 3274 }}
})
}
}
Access the route like:
this.$router.push('/pinned')
The above example assumes you give your Article route a name: 'article' property so you can redirect to it
Alias
You can keep the URL as /pinned using alias. Normally the alias would go on the existing Article route definition, but that doesn't work well with route params. You can use a "reverse alias" with a new route:
methods: {
pinRoute() {
this.$router.addRoute({
path: '/params/3274',
name: 'pinned',
alias: '/pinned',
component: () => import('#/views/Article.vue') // Article component path
})
}
}
Access the route like:
this.$router.push('/pinned')
Notes:
You'll probably want to pass an id argument to the pinRoute methods rather than hardcode them like in the examples above.
A nice thing about addRoute with either method above is if the route already exists, say, from the last time you called the method, it gets overwritten. So you can use the method as many times as you like to keep changing the destination of /pinned. (The docs in both Vue 2 and Vue 3 say the route definition will get overwritten, though Vue 2 router throws a duplicate route warning.)
Of course the pinned route won't automatically persist between app refreshes, so you'll need to save/load the pinned id (i.e. using localStorage, etc.) and run one of these methods on app load if you want that

vue and webpack doesn't do lazy loading in components?

The lazy component in vue/webpack seem to be wrong or I miss confuse about the terms.
To do lazy loading of component I use the keyword import and webpack should split this component to sepeate bundle, and when I need to load this component webpack should take care of it and load the component.
but in fact, webpack does make sperate file, but it loaded anyway when the application is running. which is unexpected.
For example I just create a simple vue application (using the cli) and browse to localhost:8080/ and the about page should be loaded (by default) using the import keyword.
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
// route level code-splitting
// this generates a separate chunk (about.[hash].js) for this route
// which is lazy-loaded when the route is visited.
component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
}
]
So This is by design? I load every time the file I do not need right now (the page about). and if I have 200 pages, when I'll fetch 200 files I dont need. how that ends up? that did I save here by using the import key?
In vuetify for example they uses import key, but the files are loaded anyway and not by demand.
You can also avoid component prefetch using one of the webpack "magic" comments (https://webpack.js.org/guides/code-splitting/#prefetchingpreloading-modules), eg.
components: {
MyComponent: () => import(/* webpackPrefetch: false */ './MyComponent.vue')
}
Feel free to read more about this Vue optimization below:
https://vueschool.io/articles/vuejs-tutorials/lazy-loading-individual-vue-components-and-prefetching/
If you're referring to the default app template from Vue CLI, then you're actually observing the prefetch of the About page, intended to reduce load times for pages the user will likely visit.
If you want to avoid this performance optimization, use this Vue config:
// vue.config.js
module.exports = {
chainWebpack: config => {
config.plugins.delete('prefetch')
config.plugins.delete('preload')
}
}
For troubleshooting reference, Chrome's Network panel includes an Initiator column, which provides a clickable link to the source file that triggered the network call. In this case of the about.js, the source file looks like this:
try using vue-lazyload maybe it can help and for <script> tags you can try async defer it helps in website speed optimizations

Data passing using props in router is not working

I am trying to send data from one vue component to another by using props in router. but it is not working. whenever i try to log the props it outputs undefined. code is given below
From where data is sending
Where receiving
in index.js. router setting
None of the code you've posted matches up.
Firstly, the console logging should be just console.log(this.myprops). The point of using props is that you don't need to reference the router itself, e.g. via $router.
Next problem, you're mixing path and params. That isn't allows. See https://router.vuejs.org/guide/essentials/navigation.html. params are for named routes.
I imagine what you're aiming for is something like this:
self.$router.replace({ name: 'DashboardPatient', params: { myprops: authUser.email } })
with router config:
{
path: '/patient',
component: Dash,
children: [
{
path: ':myprops', // <--- Adding myprops to the URL
name: 'DashboardPatient',
component: DashboardPatient,
props: true,
meta: { requiresAuth: true }
}
]
}
Keep in mind that routing is all about building and parsing the URL. So the value of myprops needs to be in the URL somewhere. In my example it comes at the end, so you'll get /patient/user#example.com as the URL. If it weren't in the URL then there'd be no way for the router to populate the prop if the user hit that page directly (or refreshed the page).
To hit the same route using a path instead of a name it'd be something like this:
self.$router.replace({ path: `patient/${encodeURIComponent(authUser.email)}` })
or even just:
self.$router.replace(`patient/${encodeURIComponent(authUser.email)}`)
Personally I'd go with the named route so that the encoding is handled automatically.
If you don't want to put the data in the URL then routing is not the appropriate way to pass it along. You'd need to use an alternative, such as putting it in the Vuex store.

Vue router not navigating

I'm having problems getting VueRouter to navigate.
Within my app some pages work fine, and with identical code, other pages the routing doesn't work / page doesn't update navigate.
Are there some gotchas with the router? Like you cannot call the router from within components or... ?
named route in my app
export default new VueRouter({
routes: [
{
path: '/grams/one/:cname',
component: GramsOne,
name: "gramsOne"
},
...
Then inside a component on a page:
<q-btn v-for='(rel, key) in gram.relatedItems' :key='key'
color='secondary'
#click="goGram(rel)"
>
{{rel.cn}}
</q-btn>
// later in the script:
methods: {
goGram(gram) {
let newRoute = {
name: 'gramsOne',
params: {cname: gram.cname}
}
console.log('goGram', newRoute)
this.$router.push(newRoute)
}
},
Elsewhere on the same page, simple routes work.
The URL address will get updated in the browser.
I see the right console log with route info
But the page/content will not change.
Once the URL bar has been updated, I can hit ctrl-R and the new page will get loaded. So the destination route is working fine.
From other pages in the same app I can use the same route to target new destination and loads fine.
This is also loading with the same URL and just a query param different that is causing the problem.
/app/items/foo
/app/items/bar
where bar is a type of some property /app/items/:foo
I have tried various combinations of named routes, routes using etc, and can't really see a pattern.
"vue": "~2.5.0",
"vue-resource": "^1.3.4",
"vue-router": "^2.7.0"
Thanks any hints!
The Component in this case is the same, so vue will reuse the instance.
The this.$route in the component will change but created(), beforeMounted() and mounted() hooks won't be callled.
Which is probably where you use the this.$route.params.cname
To force vue to create a new component instance you can set a unique key on the like <router-view :key="$route.fullPath">
Another options is to react to changes in the $route with a watcher:
watch: {
"$route.params.cname": {
handler(cname) {
// do stuff
},
immediate: true
}
}
Router can be called within a component just fine. In fact, you usually call router from the component.
From the code snippets provided, at one point in the code you use cn, then at other point you use cname. That might be the problem why it never quite work for you.
I create a codesandbox to recreate your scenario, and besides that minor naming mentioned above, things seem to work just fine.