Faced such a problem - I send data to the props of the /router-link/ tag, when I click on the link, I go to the article, it gets the data, everything works. Well, if you press the "back" button in the browser and then "forward" there will be no articles, there will be empty fields without data. How can this be avoided?
This is link to the article
<h3 class="database-article__title">
<router-link
:to="{name : 'article',params: {
id: item.id ,
type:item.type ,
name: item.name,
text: item.text,
author: item.author,
isFavorite: item.isFavorite
}}"> {{item.name}} </router-link>
</h3>
Little part of article-template.vue
<div class="content-type marketing">
{{$route.params.type}}
</div>
<h3 class="database-article__title">
{{$route.params.name}}
</h3>
<div class="database-article__text">
{{$route.params.text}}
</div>
Once again, the data transfer is good, when you click on the link, everything is displayed. The problem is that when clicking on the buttons in the browser "back" and "forward" - the browser history is not saved.
Does anyone know the solution to the problem, or where i can read how to solve it?
Thanks!
My guess is that your article route does not specify any of those params in its path. When you click the link, vue-router will remember the params object you specified in the <router-link> and will be accessible through $route.params in the article component.
However, when you click the browser back then forward buttons, the transition to the article route did not occur by clicking the <router-link> like it did the first time, and since those params were not included in the route's path, $route.params will be empty.
I'm guessing you're just trying to pass data from one route to another. If you want it to persist across history state changes (i.e. browser back/forward), then either:
The data needs to be included in the URL, either as params (e.g. /article/:id/:type etc, this needs to be specified upfront in the route's path) or in the query string (e.g. /article?id=1&type=foo). This isn't ideal for this situation.
(Recommended) Store the item object in such a way that it can be accessed by any route. Vuex is one way, but this may be overkill.
Realistically your URLs should only need to have the article's ID in it, like this /article/1. All the other stuff like type/name/etc don't belong in the URL. From the ID you should be able to fetch the full article object either from a REST API (XHR request), or obtain it from some in-memory data store abstraction (Vuex or anything else really).
Related
The web app I'm working on has a log in page where it asks users to select which site they wish to use, this site selection is done as so:
<v-select v-if="step > 0" v-model="userId" label="Select Account" :items="accounts" item-text="owner.name" item-value="id" :rules="[rules.isRequired]" :disabled="step > 1" /> (The site names provided in the dropdown are saved in a mysql table called owners).
The task at hand is to make the name of the site they chose display on every page. To do this, I am aiming to put the name in the nav bar at the top, as so:
<v-toolbar-title class="d-flex text-center"> NAME_HERE </v-toolbar-title>
I currently cannot figure out how I would bring the site name that they chose and display it how I wish to do so, and would really appreciate the help.
sidenote: the login code is from a login.vue file and the name display part is in AppBar.vue.
There are multiple ways to achieve your goal.
Pass value from the root component
You can store selected values from a user by using callback in login.vue. Callback user should be called when a user changes the site name, then pass values back to the main component (in this case might be App.vue).
But, This method is really ugly if you have 5 nested vue components, so you have to pass a callback function down through 5 components. So let talk about a next solution.
Vuex, the global store
You can store values from anywhere by anyone inside a state store. There is a library named Vuex. It allows you to save values that any component can access.
In this case, you can update the site name via login.vue then AppBar.vue access it on its own.
Read more about it here. https://vuex.vuejs.org/
However, Vuex kinda makes your app more complicated. Use it when there are no other ways you can think of.
This is a follow up to #1458. I'm looking for some direction on how Nuxt expects this to be handled.
I have a menu. When I click on a nuxt-link in the menu, I want to have time to close the menu before the page transition happens. The thing is, I only want that to happen when you click on the nuxt-link in the menu, not every time I go to a certain route (as the previous issue described using a middlewear on the route).
So there are a few different ways to do this, and I'm curious what the "Nuxt" way is?
The way we currently do this, disable the nuxt-link and capture the click, then do a router.push().
<nuxt-link :to="path" event="disabled" #click.native="delayLoad"/>
// Methods
delayLoad(event) {
this.$store.commit("CLOSE_MENU")
setTimeout(()=>{
this.$router.push(event.target.pathname)
}, 2000)
}
Is this a good idea? I just always have an aversion to hijacking nuxt-link and browser navigation like this. It seems janky.
The other ideas we played with were using a query param on the nuxt-link, and then using that in a middlewear to delay the page transition. That seemed worse to me, because now my URL's have a query param in them that is used for an animation, seems like that is abusing query params. This also triggers the page loading progress bar, which isn't really the intent, it's to have a sequenced animation happen, then page load.
It seems to me that perhaps nuxt-link should have a delay prop, or perhaps the page transition config should allow for a delay (like it does with duration)?
I wanted to do this as well and came up with the following solution. Using the new slots api you can more elegantly customise the nuxt-link behaviour:
<nuxt-link v-slot="{ route, href }" :to="path" custom>
<a :href="href" #click.prevent="$emit('navigate', route)">
<slot></slot>
</a>
</nuxt-link>
This will make the link emit a navigate event with the route as a param. You then listen for this event wherever you include your menu component, like this:
<template>
<transition
name="fade"
#after-leave="maybeNavigate"
>
<MainMenu
v-if="menuIsVisible"
#navigate="navigate"
/>
</transition>
</template>
<script>
export default {
data: () => ({
menuIsVisible: false,
navigateToOnMainMenuClose: null,
}),
methods: {
navigate(route) {
this.navigateToOnMainMenuClose = route
this.menuIsVisible = false
},
maybeNavigate() {
if (this.navigateToOnMainMenuClose) {
this.$router.push(this.navigateToOnMainMenuClose)
this.navigateToOnMainMenuClose = null
}
},
},
}
</script>
Whenever you click a nav link in the menu, the route will be stored and the menu will close. After the menu out animation has finished, maybeNavigate() will push the stored route, if there is one. This removes the need for a setTimeout and if you manage to click multiple links in quick succession only the last one will be stored and navigated to.
Since nuxt-link is essentially a wrapped version of vue's router-link, if you look at the documentation for that there is an event property that accepts string or string[], looking at it's source code here: https://github.com/vuejs/vue-router/blob/dev/src/components/link.js#L86
you can see it will register a listener for disabled in this instance. It may make more sense to pass an empty array so that no event listeners are registered, but that's at the cost of readability.
Otherwise, #click.native is the suggested way to handle custom click handlers for router-link (see: https://github.com/vuejs/vue-router/issues/800#issuecomment-254623582).
The only other concerns I can think of are what happens if you click 2 different links in rapid succession and what happens if you click more than once. May just want to add a variable to track if a link has been clicked to prevent firing setTimeout multiple times, which could navigate you from page A to B and then to C as all timeouts will fire if not canceled. Or maybe you want to only navigate to the 'last' link clicked, so if another link is clicked, you would cancel the earlier setTimeout. This is realistically an edge case that probably won't be an issue, but worth exploring.
Otherwise, IMO, looks good to me. This seems like the simplest way to implement this without having to create a custom component / plugin. I'm no expert, but is likely how I would implement this functionality as well. It would be nice to see a delay option though since I can see myself using that functionality as well with vuetify.
Another potential method would be to do your store commit in beforeTransition: https://nuxtjs.org/api/configuration-transition/
Though I'm not sure that there is access to the store there, so you might have to write a custom plugin for that as well. Again, seems more complicated than it's worth for a simple delayed animation. Simple, working code is sometimes the best solution, even if it's not the most extensible option.
See also: How can I transition between two nuxt pages, while first waiting on a child component transition/animation to finish?
for another way of handling this.
I'm making an app using VueJS and Framework7, and I'm having trouble understanding how to apply dynamic route matching to my app.
My app has two pages, main view and info page. On the main page, there is a list of links that all lead to the info page. However, the links are generated from API data, and I wish to do the same on the info page. What I'm trying to do is pass the id parameter from the API data into the link address, so that it's stored in there even while I load the same info page template. Using that id, I'd like to identify which data to print on the info page from the API data.
Here is my link element:
<f7-list-item v-for="lowerBoss in lowerBosses" :key="lowerBoss.id" :data="lowerBoss" class="single-boss subheading white" link="/boss/lowerBoss.id" onclick="console.log(lowerBoss)">
{{ lowerBoss.name }}
</f7-list-item>
So here I am trying to pass the id from the lowerBoss object into the link address and key. I tried to console.log the object as well, but whenever I click on this link, I get an error saying lowerBoss is not defined.
I am aware that I should most likely be using router-link for this, but I had trouble getting that to work - the links would not work wherever they led. Besides that, I had the same issue with them too.
The answer above is right. You have to remmeber, that every property that written as usual <component link="some-link/object.id"></component> will not be parsed, but will be passed as string. So you have to use :link="'/bla/bla/'+object.id".
lowerBoss will be available inside f7 component as "data", because of :data="lowerBoss" this part of your tag.
Check this Vue.js Passing Static or Dynamic Props
To handle events you have to use vue.js directives Event handling
Try as below
<f7-list-item v-for="lowerBoss in lowerBosses" :key="lowerBoss.id" :data="lowerBoss" class="single-boss subheading white" :link="'/boss/'+lowerBoss.id">
{{ lowerBoss.name }}
</f7-list-item>
I'd like to my "SPA" refresh when I click a link, instead of its default behavior where you see the minimal component replacement.
It sounds like I'm killing the best part of SPA, but I feel that the links without refresh cause more troubles than benefits - the worst one I suppose is that the page doesn't apparently respond if you click a link to the current page, which is rather confusing in my opinion:
<!-- You click, then of course nothing happens when you're at HelloWorld -->
<router-link :to="{name: 'HelloWorld'}">link</router-link>
Okay, I didn't know that a normal <a> tag works as my expectation if history mode is used, so I ended up doing this:
<a :href="$router.resolve({name: 'HelloWorld'}).href">link</a>
or maybe just:
link
Thanks akai, I use your help in the case when I want to use the same component. I have a list of categories and when I click on a certain category page needs to be reloaded.
<a :href="$router.resolve({name:'category', params: {id: category.id}}).href">
{{category.name}}
<span>({{category.posts.length}})</span>
</a>
Background
I have a typical web app with a left column menu; and a right column for content.
If I put a menu in the left column and use <router-view></router-view> in the right, as you would expect the content is swapped out as you navigate routes.
See a basic gist.run here. This is obviously not my actual app so ignore the oversimplifications. You can see by clicking on one of the menu items it changes style, then click one of the home/page links and the menu item keeps its applied class.
The problem
If I add a layout for these two routes, the menu looses its state when changing routes. i.e. the applied "active" style is lost because the layout is reloaded, instead of just the slot being reloaded. gist.run here:
app.js:
config.map([
{ route: '', name: 'home', moduleId: 'home', layoutView: "layout.html", layoutViewModel: "layout" },
{ route: 'page', name: 'page', moduleId: 'page', layoutView: "layout.html", layoutViewModel: "layout" }
]);
layout.html:
<template>
<!-- left menu content here -->
<div class="layout">
<!-- route view on right: -->
<slot></slot>
</div>
</template>
My home.html and route.html pages are identical to the previous gist without layouts:
<template>
Home
</template>
Note that slots are not needed here (e.g. <div slot="name">...) because there is only one slot. I've tried including them and the result is the same.
I would expect that the layout/router behaviour should look the same - that the layout isn't reloaded on each route change - even though it's wired differently underneath.
This is a problem because I may have animations on the menu items (sliding, dropdowns, active classes etc.) and I don't want those starting from the default position each time a route is changed. I may also have controls in the left panel such as a search box or other form, and may not want that refreshing on each route change.
Is there any way I can make the layout behave the same (keep "state") as the router-view?
As a way around it I've considered:
an initial state that I put together from the current route (could get messy or not cover all options)
saving state in a service or singleton somehow, but I can't figure out if this is possible with a view
router viewports. Not quite as flexible because I can't have multiple different app.html's with different viewports laid out in different ways. It also requires a module-per-viewport or other trickery, and can't have all the content from the same view as with slots...
Please tell me if there's something else I can do or if I'm doing it all wrong!
thanks.
I just yesterday asked about this over at the Aurelia Gitter room, and the response I got was that layout lifecycle is tied to the routed module's lifecycle. In practice, this means that:
If your activationStrategy is refresh, the layout will be destroyed and recreated even when navigating to the same route with different parameters.
If your activationStrategy is invokeLifecycle, the layout will not be destroyed and recreated if you navigate to the same route, but it will be if you navigate to a different route.
So if you have some state that you want to keep in the parent structure, and still be able to have multiple positions where content is projected on a per-route basis, viewports are the way to go, with the caveats that you already noted.