I have the component: same route but different parameter
<router-link :to="{name: 'menu1-mostrar', params: {atr: var1}}">
Menu1
</router-link>
<router-link :to="{name: 'menu1-mostrar', params: {atr: var2}}">
Menu2
</router-link>
I'm not getting the component to update automatically. I can only update by clicking on another menu item that has the different route.
Note that menu 1 and menu 2 use the same route. When I click menu 1 and then if I want to click menu 2 it does not call the route again. I have to call for example menu 3 that uses different route and then menu 2 I need the route always be called independent if I am already in it.
Because the route is the same, the component does not have to be changed, from a vue-router point of view.
Your component must be reactive to the parameter: Change your component values when the parameter change.
Solution 1: Watch for your parameter change, inside the component.
watch:{
atr(newValue, oldValue){
//handle change, maybe refresh
}
}
Solution 2: Use your parameter inside computed values:
computed:{
myBusinessValue(){
return this.atr + " is the value of the param atr!!!"
}
}
Depending of what happend when the parameter change, you will use sol1 or sol2. You can give more details about that to guide you on one or the other.
You need to watch the attribute through the $route object, like so:
watch: {
'$route.params.atr': {
handler () {
// Do stuff
}
}
},
Related
I am trying to navigate the router from methods in Vuetify, but when putting a /path on the go() function, the whole page just refreshes, and navigates nowhere.
Works:
<v-btn router to="/somepage">Goto page</v-btn>
Doesnt work (page just refreshes):
this.$router.go('/somepage')
Works (history go one step back):
this.$router.go(-1)
Browsing the $router object in the Chrome console, it looks just fine. What am I doing wrong?
Vue router provides several functions for programmatic navigation.
this.$router.go(n)
The go(n) function expects n to be a number and is the number of steps forward or backward in the history to travel. I think this is the reason you're not getting the result you expect.
this.$router.push(path|route)
If you'd like to supply an actual path to the function, you should use push() instead of go(). For example to go to the "home" page you might use this.$router.push('/') or to go to the profile page this.$router.push('/profile'). Importantly, when you use push() the path you submit gets added onto the history stack.
this.$router.replace(path|route)
If you do NOT want to add an entry to the history stack, you should use replace() instead of push(). Otherwise, the usage of replace() is the same as push(), i.e. this.$router.replace('/') or this.$router.replace('/profile').
Using a path vs using a route object
When navigating with push(path|route) and replace(path|route) you have the option to use a path, which is just a string that is the URL of the place you want to go, or using a route object. The route object gives you a lot more control and allows you to send route or query parameters to the destination component.
For example, if you want to navigate to the profile page for the user with an id of 3, you could do something like this.$router.push({ path: '/profile', params: { id: 3 } }). Alternatively, if you're using a named route, you could substitute the route name for the path, i.e. this.$router.push({ name: 'profile', params: { id: 3 } }). The name should correspond to the name assigned to the route wherever you've set up your main Router instance.
Using Vuetify's v-btn component
If you're using a Vuetify button component there is NOT a router attribute. Instead you'd just specify the to attribute and Vuetify should automatically interpret it as if it were a router-link, e.g. <v-btn to="/profile">My Profile</v-btn>. Alternatively, you can use a route object, e.g. <v-btn :to="{ path: '/profile', params: { id: 3 } }">Profile</v-btn>. Importantly, notice the colon (:) preceding the to in the second case. This tells Vue to interpret the attribute value as JavaScript instead of plain text.
If I’m on a page with the URL 'http://localhost:8080/item' and I’m clicking on the same link on this page, then the page does not reload.
I need to make that if I click on the same link, the page will reload.
My link:
<nuxt-link :to="/item">
Any insight will be welcome. Thanks!
Use key, something like:
<router-view :key="$route.params.yourCustomParam"/>
Also you can use something like:
<router-link :to="{ params: { yourCustomParam: Data.now } }" replace>link</router-link>
Remember to is passed router.push() and it accept an object also. Doing that, it is more declarative and controllable. I'm using this to decide if the page of component should be rerendered since they will based on id params obtained from URL entry, and my child component can still using nesting .
I recently tried to solve a similar issue and to overcome this I used Vuex with :key (ref).
Firstly, in your store you need a state property such as:
export const state = () => ({
componentUpdates: {
item: 0,
//can add more as needed
}
})
In general, you could use only one property across the app if you prefer it that way. Just remember that later on, the key value needs to be unique - that is in the case if you used this property for two or more components within one page, for example. In this case, you could do something like this :key="$store.getters.getComponentUpdates.item+'uniqueString'"
then a getter:
export const getters = {
getComponentUpdates(state) {
return state.updateComponent;
}
}
finally a mutatation:
export const mutations = {
updateComponent(state, payload) {
return state.componentUpdates[payload.update]++
}
}
Now we can utilise the reactive :key wherever needed.
But first in your nuxt-link lets add an event to trigger the mutation, note the usage of #click.native to trigger the click event:
<nuxt-link #click.native="$store.commit('updateComponent', { update: 'item'})" :to="/item">
Now in the item page, for example. Let's imagine there is a component that needs to be updated. In this case we would add :key to it:
<my-item :key="$store.getters.getComponentUpdates.item" />
That is it. As you can see this solution utilises the benefits of nuxt-link but also allows us to selectively update only parts of our page that need updates (we could update the entire page this way as well if needed).
In case if you needed to trigger the logic from mounted or initial load in general, then you could use computed property and :key to your div container, right inside the <template> of your page.
Add :key to the div:
<template>
<div :key="$store.getters.getComponentUpdates.item"></div>
</template>
Create computed property:
computed: {
updateItemPage() {
//run your initial instructions here as if you were doing it in mounted then return the getter
this.initialLoadMethod()
return this.$store.getters.getComponentUpdates.item
}
}
The final touch, which is not crucial but can be implemented in order to reset the state property:
export const mutations = {
updateComponent(state, payload) {
return state.componentUpdates[payload.update] >= 10
? state.componentUpdates[payload.update] = 0
: state.componentUpdates[payload.update]++
}
}
I have a Vue component that contains a list of objects named lines. I build a table from those lines using different components based on the line type. This works perfectly. Here's a stripped down version of the component:
<template>
<table>
<tr v-for="line in lines"
:key="line.key"
:is="componentForType[line.eventType] || 'LogLine'"
v-bind="line"
/>
</table>
</template>
<script>
export default {
name: 'DebugLog',
components: {
LogLine,
FormattedLogLine,
UserDebug,
Limits
},
data () {
return {
lines: [],
selectedKey: null,
componentForType: {
'USER_DEBUG' : 'UserDebug',
'LIMIT_USAGE_FOR_NS' : 'Limits',
'EXCEPTION_THROWN' : 'FormattedLogLine',
'FATAL_ERROR' : 'FormattedLogLine'
}
}
},
mounted() {
// code that loads this.lines
}
}
</script>
Now I want to be able to click any row of the table, and have the row become "selected", meaning that I want store line.key in this.selectedKey and use CSS to render that line differently. But I can't get the events working. Here's the updated <template>; nothing else is changed:
<template>
<table>
<tr v-for="line in lines"
:key="line.key"
:is="componentForType[line.eventType] || 'LogLine'"
v-bind="line"
:class="{selected: line.key == selectedKey}"
#click.capture="selectedKey = line.key"
/>
</table>
</template>
I've added the last 2 properties on the tr element - a dynamic class binding and a click event handler to set this.selectedKey to the active line's key. But it isn't working. I replaced the #click handler code with console.log(line.key) and nothing is logged, which tells me that my #click handler is never firing. I originally wrote it with out the .capture modifier, but tried adding the modifier when the original didn't work.
Is vue.js stopping propagation from the child component to the parent? Can I not bind the click event on the tr since it :is another vue component? Or is there something else going on? The examples I've found in the docs are much simpler and I'm not sure they correspond to my situation. The various child components are not binding any click events. I'd prefer to handle the event entirely in the parent as shown, since I will have a number of types of child component, and I don't want to have to implement click handlers in each.
Update: Looking at my child components, I note that each contains a tr tag that must effectively replace the tr in the parent template. For example, my most basic component is LogLine, shown here:
<template>
<tr>
<td>{{timeStamp}}</td>
<td>{{eventType}}</td>
<td>{{lineNumber}}</td>
<td>{{lineData}}</td>
</tr>
</template>
<script>
export default {
name: 'LogLine',
props: ['timeStamp', 'eventType', 'lineData', 'lineNumber'],
data: function () {
return {}
}
}
</script>
So I'm guessing that the binding in the parent isn't actually binding on the tr in the DOM; it's just binding on the Vue component, listening for a click event to be sent from the child with $emit; and that each child component will need to bind #click on its tr and emit it to the parent. Assuming I'm right, is there any shortcut I can use from the parent template to have vue forward the DOM events? Any other option I'm missing besides binding click in every child component?
Piggy-backing off of Jacob's answer here. Since you're essentially attaching an event listener to a dynamic component it expects a custom click event. So you have two options here:
Listen for the native DOM click event within that component (by attaching a click event listener to a normal DOM element within the component) and emit a custom click event to the parent.
Use the .native modifier to listen for the native DOM click event instead of a custom one directly in the parent.
Since you are using an :is prop, it's considered a dynamic Vue component, not a DOM element.
Events listener on a Vue component won't be passed down to its DOM element by default. You have to do it manually by going into the component template and add v-on="$listeners".
demo: https://jsfiddle.net/jacobgoh101/am59ojwx/7/
e.g. <div v-on="$listeners"> ... </div>
#Jacob Goh's use of v-on="$listeners" is simple and allows forwarding of all DOM events in one action, but I wanted to document an approach I tried on my own for completeness. I will be switching to Jacob's solution in my component. I am now using Husam's .native modifier in the parent as it is more suitable to my particular use case.
I was able to make my component work by editing each child component, capturing the click event and re-emitting it. For example:
<template>
<tr #click="$emit('click')">
<td>{{timeStamp}}</td>
<td>{{eventType}}</td>
<td>{{lineNumber}}</td>
<td>{{lineData}}</td>
</tr>
</template>
<script>
export default {
name: 'LogLine',
props: ['timeStamp', 'eventType', 'lineData', 'lineNumber'],
data: function () {
return {}
}
}
</script>
I am working on a vuejs SPA.
I have a view that shows a list of items and another view that shows details for a specific Item.
when I click the item I switch views using:
this.$router.push('/item/' + event.ItemId );
The data is managed using vuex modules.
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
Where would be the appropriate place (code/lifecycle) to trigger the (async) retrieval of the data required for rendering the item details view?
I would like to allow some temporary display while the item details are being retried (i.e. not to block the rendering of the item details view which should know on its own to indicate that it is still awaiting data).
One way to achieve this, is to define a state variable, named e.g. isLoading, in the data context of the Vue component. This variable would then be true while the data is retrieved asynchronously. In the template, you can use v-if to display a spinner while loading, and displaying the content after that.
If you are retrieving the data multiple times (refreshing the view), I would move the retrieving code into a method, e.g. called loadData. In the mounted section of the Vue component you then can just initially call this method once.
Here is some example code:
<template>
<div>
<button #click="loadData" :disabled="isLoading">Refresh</button>
<div class="item" v-if="!isLoading">
{{ item }}
</div>
<div class="spinner" v-else>
Loading...
</div>
</div>
</template>
<script>
import HttpService from '#/services/HttpService';
export default {
name: 'item-details',
data () {
return {
isLoading: false,
item: {}
};
},
methods: {
loadData () {
this.isLoading = true;
HttpService.loadData().then(response => {
this.item = response.data;
this.isLoading = false;
}, () => {
this.item = {};
this.isLoading = false;
});
}
},
mounted () {
this.loadData();
}
};
</script>
And I would also have to consider that it should work if the URL is changed (I think I read that there is an issue with the view not being reloaded/recreated when only the item id would change in the URL.
This issue you mentioned occurs if you are not using the HTML5 history mode, but an anchor (#) in the URL instead. If you are just changing the part after the anchor in the URL, the page is not actually refreshed by the browser. The Vue component won't be reloaded in this case and the state is still old. There are basically two ways around this:
You are switching from anchors in the URL to a real URL with the HTML5 history mode, supported by the Vue Router. This requires some back-end configuration, though. The browser then does not have this faulty behavior, because there is no anchor. It will reload the page on every manual URL change.
You can watch the $route object to get notified on every route change. Depending on if the user is changing the part after the anchor, or before, the behavior is different (it also depends where the cursor is, when you hit enter). If the part after the anchor is changed (your actual Vue route), only the component is notified. Otherwise, a full page refresh is made. Here's some example code:
// ...inside a Vue component
watch: {
'$route' (to, from) {
this.loadData();
}
}
I have a component myHello:
<temlate>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
</template>
And main component:
<h1>my hello:</h1>
<my-hello><my-hello>
After rendering shows this:
<h1>my hello:</h1>
<div>
<h2>Hello</h1>
<p>world</p>
</div>
How to delete <div> ?
With VueJS, every component must have only one root element. The upgrade guide talks about this. If it makes you feel better, you are not alone. For what it's worth the components section is a good read.
With the myriad of solutions to your problem, here is one.
component myHello:
<temlate>
<h2>Hello</h1>
</template>
component myWorld:
<temlate>
<p>world</p>
</template>
component main
<h1>my hello:</h1>
<my-hello><my-hello>
<my-world><my-world>
Vue gives you the tools to do so by creating templates or you can do it by having a parent div with two parent divs as children. Reset the data from the data function. Stick with convention (create templates). It's hard to get used to use Vue when you have a jQuery background. Vue is better
Ex.
data () {
message: 'My message'
}
When you click a button to display a new message. Clear the message or just set the message variable with a new value.
ex. this.message = 'New Message'
If you like to show another div. you should used the if - else statement and add a reactive variable. Ex. showDivOne
data () {
message: 'My message'
showDivOne: true,
showDivTwo: false
}
Add this reactive variables to the parent divs that corresponds to the div.
When clicking the button, you should have a function like...
methods: {
buttonClick () {
this.showDivOne = false
this.showDivTwo = true
}
}
I think you can use v-if directive to controll. Add a flag to controll status to show or hide