Browser navigation not loading component in Vue.js app - javascript

I'm building a single-page application in Vue.js. Currently, navigation through the site works properly until you attempt to use the browser navigation buttons (back/forward).
When attempting to navigate with these no pages will be created. The URL will change but no components are loaded, unless you backup to the base URL where the component is loaded.
The templates are not loaded at all, I also have ESlint which shows no errors.
Here is my index.js for the router:
Vue.use(Router);
export default new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'search',
component: Search,
},
{
path: '/results?terms=:terms',
name: 'results',
component: Results,
},
{
path: '/review?id=:id',
name: 'review',
component: Review,
},
],
});
I change pages by using: this.$router.push({ name: 'results', params: { terms: this.terms } });
I'm very new to Vue so I'm pretty sure I've just made a stupid mistake, but I've spent way too much time trying to figure this out so some help would be nice. Thanks.

The issue here is that route params should not be passed as query string parameters. They are solely intended for use in the URL path.
For some reason, the router is able to handle programmatic navigation but not direct URL loading.
If you still want to use the query string (as opposed to path parameters), I suggest you change to something like this...
Define props for your components, eg
export default {
name: 'Results',
props: ['terms'],
// etc
Pass the query string variables as props in your route definition
{
name: 'results',
path: '/results',
component: Results,
props: route => ({ terms: route.query.terms })
}
Set query instead of params in your programmatic navigation
this.$router.push({ name: 'results', query: { terms: this.terms } })

Related

In Angular, how can one component to have multiple HTML templates?

I am developing an ecommerce application, and one major feature is that this app should have multiple themes. The total number of themes could be 100 or even more. However, these themes all have the same data (For example: all home page have same banner images, new product, feature product data.) .
I know I can use ng-template or TemplateRef to determine which piece of HTML should display. But since I have over 100 themes, both ng-template or TemplateRef methods will load a lot of extra files. So I think I need some sort of lazy load, when a component loads the data then lazy loads the correct HTML template. So how can I have this work?
Looks like it is possible, all our routes are handled by lazy loaded modules. This is our out-of-the-box route config:
const routes: Routes = [
{ path: '', loadChildren: () => import('./lazy/lazy.module').then(m => m.LazyModule) }
];
While module lazy has this route config:
const routes: Routes = [
{ path: 'home', component: HomeComponent },
]
While HomeComponent is taken from the declarations of module lazy.
Then define another module, called for example lazy-two with the same route config, and its own HomeComponent.
Finally, you can switch between the modules by using this code:
lazyLoad() {
const routes: Routes = [
{
path: '',
loadChildren: () => import('./lazy-two/lazy-two.module')
.then(m => m.LazyTwoModule)
}
];
this.router.resetConfig(routes);
this.router.navigateByUrl('/home');
}
This will lazy load module lazy-two and refresh the route to /home - you will see the component of the new module displayed.
I couldn't create a stackblitz, some errors occurred probably because of lazy loading. So I ran it locally on my machine and pushed the code to GitHub
EDIT I managed to make a StackBlitz
I recommend used ComponentFactoryResolver to create the components that you need to render.
this.templates = [
{
id: "template-1",
component: Template1,
},
{
id: "template-2",
component: Template2,
},
];
ngOnInit() {
this.templates.forEach((element) => {
this.containerReference.createComponent(
this.factoryResolver.resolveComponentFactory(element.component)
);
});
}
in the .html you should have
<ng-container #containerReference><ng-container>
what about using the same component and styling it different when you select the template?

Redirect on startup by the given browser URL

I created a new Vue app with a base path. Due to the fact the app is embedded into another Vue app, this app doesn't render the router-view on startup. If you want to know more about how or why this app is embedded into another app, please have a look here:
mount Vue apps into container of main Vue app
and my own answer to this problem:
https://stackoverflow.com/a/58265830/9945420
When starting my application, it renders everything except the router-view. I want to redirect on startup by the given browser URL. This is my router config:
import Vue from 'vue';
import Router from 'vue-router';
import One from './views/One.vue';
import Two from './views/Two.vue';
Vue.use(Router);
const router = new Router({
base: '/my-embedded-app/',
mode: 'history',
routes: [
{
path: '/',
component: One,
},
{
path: '/two',
component: Two,
},
],
});
router.replace(router.currentRoute.fullPath);
export default router;
I want the app to render the component Two when calling .../my-embedded-app/two. Unfortunately, router.replace always redirects to / (so the browser URL is .../my-embedded-app/). I debugged the router, and saw that the path is /, but I would expect it to be /two.
Important note:
I don't want to redirect the user to /two whenever the url is /. I just want to render the view Two when the url is /two. Currently it doesn't.
What might be wrong? Maybe I don't even need that redirect, and can solve the problem a more elegant way.
updated: jsfiddle
What wound up happening is that route.currentRoute.fullPath was executed before router was ready.
That's the normal behavior, it is the way single page application works. The "pound" - like # - sign means that the links will not move the application to another page.
so you can change the router mode to
mode:"hash"
Try beforeEnter. Have a look at the below code:
const router = new Router({
base: "/my-embedded-app/",
mode: "history",
routes: [
{
path: "/",
component: One,
beforeEnter: (to, from, next) => {
// Your are free to add whatever logic here.
// On first visit
next("/two");
}
},
{
path: "/two",
component: Two
}
]
});
Read more about Navigation guards here
Could you provide a github repo or some to test the actual?
Otherwise my first idea is to use children routes with an "empty" view as base, here an example about what I tried. (working in my case)
router.js
export default new Router({
mode: 'history',
base: '/my-embedded-app/',
routes: [
{
path: '/',
name: 'base',
component: Base,
children: [
{
path: '',
name: 'one',
component: One
},
{
path: 'two',
name: 'two',
component: Two
}
]
}
]
})
Base.vue
<template>
<router-view/>
</template>
One.vue
<template>
<div>One</div>
</template>
Two.vue
<template>
<div>Two</div>
</template>

When are routed components initialized?

I have the following route:
path: ':id', component: ViewBookPageComponent },
And when added it produces the error:
Error: Cannot read property 'id' of null
I'm not performing a null check in the component since the property will be available when the component is navigated to and in this case the auth guard is also keeping rerouting to login prior to any of the book routes being shown.
This is a stacblitz demo:
https://stackblitz.com/edit/angular-ngrx-slice-demo-fork-with-id-route?file=src%2Fapp%2Fbooks%2Findex.ts
If I comment out the route, the error no longer appears, so it seems Angular is instantiating the component prior to it being routed to.
Is the following fix your problem?
const routes: Routes = [
{
path: 'find',
component: FindBookPageComponent,
},
children:[
{
path: 'result',
component: SearchResultComponent,
},
children:[
{
path: 'book/:id',
component: ViewBookPageComponent ,
}
]
]
]
The AuthGuard is not protecting the route. It needs to be declared within the Book module itself. See this question for more details.

How to capture more of the path with dynamic route matching?

A link is fetched from the database, e.g. /app/profile.
A single component (master/index.vue) should render any path following the app's address (http://example.com/).
I have the following router configuration:
{
path: '/',
component: () => import('#/components/Layout.vue'),
children: [
{
path: ':id',
component: () => import('#/components/pages/master/index.vue')
}
]
}
… but this is not working as expected. It only accepts one param after example.com. How do I make it work dynamically so that it accepts multiple params, e.g. /app/profile?
you can use an asterisk to match anything, for example:
path: '*',
if you know the base path will be fixed, you can narrow the match to e.g. /*, /app*.
see the vue-router docs on dynamic matching, under the catch all section.

How to set data into nuxt.js nuxt-link?

I'm trying to pass data into nuxt-link but nuxt-link is just returning a 404 error when I click on the link. It doesn't seem to be getting and loading the file....
The second 2 links that use :href and hardcoding works
<template>
<h2 class="subtitle"><nuxt-link :to="{path: filePath}" exact>Nuxt View Menu</nuxt-link></h2>
<h2 class="subtitle"><a :href="filePath">Vue View Menu</a></h2>
<h2 class="subtitle">HardCode View Menu</h2>
</template>
<script>
export default {
layout: 'default',
data () {
return {
filePath: 'files/officialMenu.pdf'
}
}
}
</script>
Nuxt uses vue-router by reading off the vue-router documentation you'll be able to achieve what you want.
router-link documentation
Example below
<!-- named route -->
<nuxt-link :to="{ name: 'user', params: { userId: 123 }}">User</nuxt-link>
<!-- with query, resulting in `/register?plan=private` -->
<nuxt-link :to="{ path: 'register', query: { plan: 'private' }}">Register</nuxt-link>
This will be available to your next page in $route object as $route.params or in the url query as seen above.
If you use post way to send data another route in vuejs or nuxtjs.
Here, if route name is = /user
So, you have to write the following nuxt-link
<nuxt-link :to="{ name: 'user', params: { userId: 123 }}">User</nuxt-link>
and for receive data next componet, means on "/user" route you have to write inside created or any other place and check console.
created() {
console.log(this.$route.params)
console.log(this.$route.params.userId)
console.log(this.$nuxt._route.params)
console.log(this.$nuxt._route.params.userId)
}
========================================================
if you use Get way to send data another route in vuejs or nuxtjs.
Here, if route name is = /register
so, you have to write the following nuxt-link
<nuxt-link :to="{ path: 'register', query: { plan: 'private' }}">Register</nuxt-link>
and for receive data next componet, means on "/register" route you have to write inside created or any other place and check console.
created() {
console.log(this.$route.query)
console.log(this.$route.query.plan)
console.log(this.$nuxt._route.query)
console.log(this.$nuxt._route.query.plan)
}
Now, you can use this data anywhere like data, mounted, method etc...
How to define route name?????
Add the following code into "nuxt.config.js" file to add route name.
router: {
base: '/',
extendRoutes(routes, resolve) {
routes.push({
name: 'user',
path: '/user',
component: resolve(__dirname, 'pages/user.vue')
})
}
},
Here,
Name property is the name of route that you want to provide as route name.
In Path property you have to provide route path.
Component property is the component path of that component need to load in this route.

Categories

Resources