VueJS router-link doesn't update component content - api

Category component
<ul v-else>
<li
v-for="cat in getCategories">
<router-link :to="{ name: 'ProductsCategory', params: {category_name: cat.name}}">{{ cat.name }}</router-link>
</li>
</ul>
This works fine and redirect fine to the correct link.
Problem is
When it redirect it doesn't call again the state while i am using vuex.
Component script
computed: {
getLoading () {
return this.$store.state.companyInfo.loading;
},
getCompanyBasicInfo () {
return this.$store.state.companyInfo.companyBasicInfo;
},
getCategories () {
return this.$store.state.categories.categoriesName;
},
getCategoriesLoading () {
return this.$store.state.categories.loadingState;
},
getCataegoryProducts () {
return this.$store.state.products.getCategoryProducts;
},
},
created() {
this.$store.dispatch('getCategories', this.$route.params);
this.$store.dispatch('getCategoryProductsAction', this.$route.params);
this.$store.dispatch('getCompanyBasicInfo', this.$route.params);
}
It should call getCategoryProductsAction which call my API and filter due to the router-link params.

This may be normal, because this component is not destroyed, but the $route parameters have changed.
So you can watch the $route for params.category_name changed
watch: {
// when redirect to new category_name, this will be callback
'$route': (new, old) => {
if (new.params.category_name !== old.params.category_name) {
// reload data, same with created()
}
}
}
see more: https://router.vuejs.org/guide/advanced/data-fetching.html#fetching-after-navigation

I would prefer to use the watch lifecycle in Vue.js.
Basically what is does is watching your route and when it changes you can tell it to run a function.
example:
watch: {
// call again the method if the route changes
'$route': 'initService'
}

Related

Fetching data from json server in vue with axios

I would like to know is there is a way to do something like this without backend. I am calling all data from json server and displaying on home page:
async created() {
try{
const products = await axios.get('http://localhost:3000/products')
this.products = products.data
} catch(err) {
console.log(err)
}
}
Now when i click any of these products i would like to redirect user to new page and would like to display data of that specific object from json server.
What i have built for now is when user click on any product he gets redirected to route /product, and everything is hardcoded there, no dynamic data.
I hope my question is clear, thank you everybody.
You should consider using Vuex for this.
Move your method from created() to vuex's action and then call it in the hook.
The Vuex store's code is gonna be something like this:
state: {
products: []
},
getters: {
getProductById: (state) => (id) => state.products.find(product.id === id)
},
mutations: {
SET_PRODUCTS(state, products) {
state.products = products
}
},
actions: {
// this one is to call from the hook
fetchProducts({ commit }) {
return axios.get('http://localhost:3000/products').then(res => {
commit('SET_PRODUCTS', res.data)
})
}
}
Then call something like this from the component you're redirecting from:
<router-link
:to="{
name: 'Product', // im assuming this is the /product route's name
params: {
id: product.id // product.id is the id of your specific object your clicked on
}"
>
{{ product.productName }}
</router-link>
In your <Product /> component, you get the specific object by id from your Vuex store by using the getProductById() getter.
In your template:
...
<!-- the id we've passed as a route params -->
<h1>{{ getProductById($route.params.id) }}</h1>
...

Emit event from App.vue and catch in component

I am trying to implement sign out handling in Vue. I redirect to Home which works fine on all pages except Home which is not refreshed. So I decided to emit a signal and refresh data once I catch it.
App.vue
<b-dropdown-item href="#0" v-on:click="signMeOut()">Sign out</b-dropdown-item>
methods: {
signMeOut() {
this.$store.dispatch('SIGN_USER_OUT');
if (this.$route.path === '/') {
this.$emit('sign-out');
} else {
this.$router.push({ name: 'home' });
}
},
Home.vue
<b-container fluid="true" class="pt-3 w-75 m-auto" v-on:sign-out="reload">
created() {
this.$store.dispatch('INIT_STREAM');
},
methods: {
reload() {
console.log('reload');
this.$store.dispatch('INIT_STREAM');
},
},
but the signal does not reaches the Home.vue or is ignored. How can I fix it? Or do you have a better solution of this sign out procedure?
When you use the hook $emit.
You should listen to this event in $root instance from your vuejs application, $root.
So for achieve the desired result you just have to change your code to:
In your component home (I'm putting only the session script from a .vue file)
<script>
export default {
name: 'Home',
components: {
HelloWorld
},
created(){
this.$root.$once('mylogouthandler', this.logoutEventHandler)
},
methods: {
logoutEventHandler() {
console.log('exit')
//do your stuff here.
}
}
}
</script>
your component with action logout.
<template>
<div class="about">
<button #click="handleButtonClick()">logout</button>
</div>
</template>
<script>
export default {
name: 'About',
methods: {
handleButtonClick(){
console.log('clicked')
this.$root.$emit('mylogouthandler')
}
}
}
</script>
If you would like to know more, here is the documentation for handling events in vuejs.

Reload navbar component on every this.$router.push() call

I developing a login/registration system in my Vue.js app. I want the items in navbar to be updated when I call this.$router.push('/').
App.vue:
<template>
<div id="app">
<Navbar></Navbar>
<router-view></router-view>
<Footer></Footer>
</div>
</template>
Navbar component:
export default {
name: "Navbar",
data: function() {
return {
isLoggedIn: false,
currentUser: null
}
},
methods: {
getAuthInfo: function() {
this.isLoggedIn = this.auth.isLoggedIn();
if (this.isLoggedIn) {
this.currentUser = this.auth.currentUser();
}
}
},
mounted: function() {
this.getAuthInfo();
},
updated: function() {
this.getAuthInfo();
}
}
Here is how I redirect to another page:
const self = this;
this.axios
.post('/login', formData)
.then(function(data) {
self.auth.saveToken(data.data.token);
self.$router.push('/');
})
.catch(function(error) {
console.log(error);
self.errorMessage = 'Error!';
});
SUMMARY: The problem is that isLoggedIn and currentUser in Navbar don't get updated when I call self.$router.push('/');. This means that functions mounted and updated don't get called. They are updated only after I manually refresh the page.
I solved the problem with adding :key="$route.fullPath" to Navbar component:
<template>
<div id="app">
<Navbar :key="$route.fullPath"></Navbar>
<router-view></router-view>
<Footer></Footer>
</div>
</template>
Check this out from the docs:
beforeRouteUpdate (to, from, next) {
// called when the route that renders this component has changed,
// but this component is reused in the new route.
// For example, for a route with dynamic params `/foo/:id`, when we
// navigate between `/foo/1` and `/foo/2`, the same `Foo` component instance
// will be reused, and this hook will be called when that happens.
// has access to `this` component instance.
},
I expect your Navbar component is reused across routes so its mounted and updated are not called. Try using beforeRouteUpdate if you want to do some processing on route change.

Extending Vue Lifecycle Hooks

I have a special application where I would like to run a method on every component when it is mounted. So I could have the method as a global mixin or something and then simply do..
mounted(){
this.mySpecialMethod();
}
However, I was wondering if it is possible to simply extend Vues mounted hook so the method is always run on every component when it is mounted. I have not been able to find in info on this.
If you really want to have everything call your mounted hook, you can use a global mixin.
Below, we have the mixin myMixin that will log to console every time something is mounted or destroyed. When you run the example, you can see that every time the plus button is clicked, it runs both the component's mounted hook as well as the mixin's hook.
If you want to extend this so that it can be reusable as a library, you can create a plugin out of it.
const foo = {
template: "<div #click='onClick'>hello</div>",
mounted() {
console.log("Foo's mounted");
},
methods: {
onClick() {
console.log("click");
}
}
}
const myMixin = {
mounted() {
console.log("I've been mounted");
},
destroyed() {
console.log("I've been destroyed");
}
};
Vue.mixin(myMixin);
const app = new Vue({
el: "#app",
data() {
return {
foos: []
};
},
components: {
foo
},
methods: {
add() {
this.foos.push("fizz");
},
remove() {
this.foos.pop();
}
}
});
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<div id="app">
<button #click="add()">+</button><button #click="remove()">-</button>
<ul>
<li v-for="f in foos">
<foo></foo>
</ul>
</div>

vue-router same route with different param

I am on the /entries/12 route. On the same component I would like to push /entries/13 when user clicks the next button.
Code is like :
//e is the next page number.
this.$router.push({ name: 'Entries', params: { pageNum: e }});
I get below error:
If i try different route that works.
Whole code:
<template>
<div class="entries">
<generic-entries #clicked="clicked" ></generic-entries>
</div>
</template>
<script>
import GenericEntriesComp from '#/components/GenericEntriesComp';
export default{
name: 'entries-comp',
components: {genericEntries: GenericEntriesComp},
mounted(){
var params = {id : this.$route.params.pageNum}
this.$store.dispatch("getEntries",params);
},
methods: {
clicked(e){
this.$router.push({ name: 'Entries', params: { pageNum: e.toString() }});
},
loadData(){
var params = {pageNum : this.$route.params.pageNum}
this.$store.dispatch("getEntries", params)
}
},
computed: {
items(){
return this.$store.state.entries
},
},
watch: {
'$route': 'loadData'
}
}
</script>
Btw the error comes from :
Vue.config.errorHandler = function (err, vm, info) {
console.error(err);
}
I solved my problem with :key. This way, I guarantee that all router-view content will be re-rendered whenever there is a $route change.
<router-view :key="$route.fullPath" #showLoading="showLoading"></router-view>
Found the answer.
Seems vue-router works as expected. In my case there were another problem.
If I have below code also in generic component I beleive it loops and produce an error.
watch: {
'$route': function(){
this.loadData();
}
}
In my case I removed the watcher from the generic component and it worked.
If you have decided to change the route parameter programmatically, then you should use replace instead of push as like below:
//e is the next page number.
this.$router.replace({ name: 'Entries', params: { pageNum: e }});