I have two going to the same component, but I want to update an attribute of my store.js when I click on each of these .
<router-link :to="'service'" #click="origin(2)"><a class="navigation-links "><i class="fas fa-coins" /> New service </a></router-link>
<router-link :to="'service'" #click="origin(1)"><a class="navigation-links"><i class="fas fa-coins" /> Update service </a></router-link>
The code of function origin is:
function origin(newValue) {
this.$store.commit(SET_ORIGIN, newValue)
}
But it never has been called and I don't have my store updated
I don't want to have two different url, because for the users it needs to be the same action
I have tested with the event #click.native, but it doesn't work neither
Any suggestions?
Have you tried navigating inside the method after saving your data?
<div #click="origin(2)"></div>
function origin(newValue) {
this.$store.commit(SET_ORIGIN, newValue);
this.$router.replace({ name: "service" });
}
Related
I am very new to vue/nuxt and got stuck with this issue when implementing the frontend for a headless cms.
I have two routes as such
Pages
-StandardPage
->_standardPage.vue
-InfoPage
->_InfoPage.vue
My _standardPage.vue and _infoPage.vue has this code (same for now but will change) Basicly they need a guid Id to fetch page information from the Api. The page information contains different blocks that then are dynamicly generated components:
<template>
<div>
<Menu />
<div v-for="block in site.blocks" :key="block.id">
<component :is="block.blockName" :blockData="block"/>
</div>
</div>
</template>
<script>
export default{
data(){
return {
site: Object
}},
methods: {
this.site = result from async method, calling api to get page info by guid id (how do i get a hold of this guid)
}
}
now the problem is that in my menu component im doing a fetch to get the menu display name and corresponding pageId (menu items can be added/removed on a whim so I have no way of knowing beforehand those Ids), When I click a menu item i wish to pass along the page Id to my standard or info page so they can fetch corresponding data but to no success.
I have tried to $emit from child to parent but I have no success in doing that across routes. If i do a $router.push with path and query I get a nasty guid in the url and that does not look very friendly.
What would be the best way to pass my guid from my menu component to my routed pages?
If you do have a button in your parent looking like
<button #click="$router.push({ name: 'details-page', params: { guid: '123' } })">
test
</button>
and this in the child named /pages/details-page.vue
<script>
export default {
name: 'DetailsPage',
async fetch() {
console.log('route', this.$route.params.guid)
await axios...
},
}
</script>
You'll be able to load some data in the children, depending of your guid and without an ugly path.
I am trying to pass a variable from a Parent (page) component to a Child (modal) component. After reading a few examples, this works fine. The variable in question is brought in from another component as a route param. If i refresh the page, the variable is lost and can no longer be passed to the child. My question is, is the best way to persist this using the store, or is it possible to persist another way if the user refreshed? Any help would be appreciated
Parent
<b-container>
<Modal v-show="displayModal" #close="closeModal">
<template v-slot:body>
<ExpressionCreate v-show="displayModal" :lender-id="lenderId" #close="closeModal"/>
</template>
</Modal>
<div class="card" style="width: 100%;">
<div class="card-header">
<h5>{{this.lenderName}}</h5>
<b-alert :show="this.loading" variant="info">Loading...</b-alert>
</div>
<b-btn block variant="success" #click="showCreateLenderModal">Add Expression</b-btn>
....
created () {
this.lenderId = this.$route.params.lenderId
...
navigate () {
router.go(-1)
},
showCreateLenderModal () {
this.displayModal = true
},
toggleDisplayModal (isShow) {
this.displayModal = isShow
},
async closeModal () {
this.displayModal = false
}
Child
<label>lender id:</label>{{this.lenderId}}
...
props: {
lenderId: {
type: Number
}
},
You can use VueSession to persist.
Simply persist your lender_id with
this.$session.set('lender_id', this.lender_id)
and you can get it later as
saved_lender = this.$session.get('lender_id')
if (saved_lender) {
// handle saved case here
}
You can use query params in URI by doing:
$route.push('/', { query: { param: 3123 } })
This will generate the URL /?param=3123 and this will work after refresh. Or, you can use vuex and vuex-localstorage plugin, that let you persist data in the browser, and you can close the browser and open again and data will be restored.
In order for the state of application to persist, I recommend that you use vuex to manage the state and use a library that abstracts the persisting to vue in a clean way. One popular library is vuex-persist. https://www.npmjs.com/package/vuex-persist
If you dont want to use a store, VueSession, a package linked here should do the trick
Short question
The v-model which binds a string to an input field won't update in some cases.
Example
I am using Vue within a Laravel application. This is the main component which contains two other components:
<template>
<div>
<select-component
:items="items"
#selectedItem="updateSelectedItems"
/>
<basket-component
:selectedItems="selectedItems"
#clickedConfirm="confirm"
#clickedStopAll="stopAll"
/>
<form ref="chosenItemsForm" method="post">
<!-- Slot for CSRF token-->
<slot name="csrf-token"></slot>
<input type="text" name="chosenItems" v-model="selectedItemsPipedList" />
</form>
</div>
</template>
<script>
export default {
props: ["items"],
data: function() {
return {
selectedItems: [],
selectedItemsPipedList: ""
};
},
methods: {
updateSelectedItems: function(data) {
this.selectedItems = data;
this.selectedItemsPipedList = this.selectedItems
.map(item => item.id)
.join("|");
},
confirm() {
this.$refs.chosenItemsForm.submit();
},
stopAll() {
this.updateSelectedItems([]);
this.confirm();
}
}
};
</script>
The method updateSelectedItems is called from the select-component and it works fine. In the end, the selectedItemsPipedList contains the selected items from the select-component, which looks like "1|2|3" and this value is bound to the input field in the chosenItemsForm. When the method confirm is called from the basket-component, this form is posted to the Laravel backend and the post request contains the chosen items as piped list. So far, so good.
The method stopAll is called from the basket-component and it will remove all the selected items from the array. Therefore it will call the method updateSelectedItems with an empty array, which will clear the selectedItems array and then clear the selectedItemsPipedList. After that, confirm is called which will post the form again. But, the post value still contains the selected items (e.g. '1|2|3'), instead of "". It looks like the v-model in my form is not updated, which is strange because it does work when selecting items. Why is it working when adding items, and doesn't when removing all items?
I believe you have a timing issue here. The value of the properties haven't been propagated to the DOM yet, so the form submission is incorrect. Try this instead:
stopAll() {
this.updateSelectedItems([]);
//NextTick waits until after the next round of UI updates to execute the callback.
this.$nextTick(function() {this.confirm()});
}
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]++
}
}
To do something after the DOM has been updated by Vue, you use the $nextTick binding.
To do something after a css transition has completed, you can use transitionend event.
I have a dynamic list in which things are added and removed by user actions. When removed, there is a CSS animation and then I need to check the state of the DOM immediately after the element is gone.
I was thinking that the $nextTick after the transitionend would be the state of the DOM immediately after the list item is removed, but it is not.
I need to do something after the transition has ended and the element from a list has been removed from the DOM.
Right now I have:
<transition-group class="message-bus" tag="ul" name="message-bus">
<li v-for="message in messages" v-bind:key="message.id" :ref="message.id">
<div>{{message.text}}</div>
<a class="dismiss-message" #click="dismissMessage(message)">×</a>
</li>
</transition-group>
dismissMessage(message){
const vm = this;
this.$refs[message.id][0].addEventListener("transitionend", function(){
vm.$nextTick(function(){
//This is called, but appears to be called before the element is actually removed from the DOM
//I need to query the DOM immediately after this element is removed
});
});
this.messages.splice(this.messages.indexOf(message), 1);
}
In the mounted function, I have added a MutationObserver that appears to be working as needed. I'll put this here as an answer as it does technically work and may be helpful to others, but I'm still interested in a better answer if Vue has something built in for this.
mounted(){
const vm = this;
const listItemRemoved = new MutationObserver(function(e){
if (e[0].removedNodes.length){
console.log("Removed");
}
});
listItemRemoved.observe(this.$el, {childList: true});
}
Perhaps you could use a custom directive. Perform the actions you need inside the unbind hook ..
created() {
this.vm = this
},
directives: {
foo: {
unbind(el, binding) {
// Here you can perform the actions you need.
// You can access the Vue instance using binding.value (eg: binding.value.$el)
}
}
},
And in your template ..
<transition-group class="message-bus" tag="ul" name="message-bus">
<li v-for="message in messages" v-bind:key="message.id" :ref="message.id" v-foo="vm">
<div>{{message.text}}</div>
<a class="dismiss-message" #click="dismissMessage(message)">×</a>
</li>
</transition-group>