How to trigger watch AFTER I read array from db? - vuejs2

In my vue/cli 4/vuex opening page I need to fill select input with default value from
vuex store. To fill select input I need have selection items read from db and I have a problem that watch is triggered
BEFORE I read data from db in mount event.
I do as :
watch: {
defaultAdSavedFilters: {
handler: function (value) {
console.log('WATCH defaultAdSavedFilters value::')
console.log(value)
if (!this.isEmpty(value.title)) {
this.filter_title = value.title
}
if (!this.isEmpty(value.category_id)) {
this.categoriesLabels.map((nexCategoriesLabel) => { // this.categoriesLabels IS EMPTY
if (nexCategoriesLabel.code === value.category_id) {
this.selection_filter_category_id = {code: value.category_id, label: nexCategoriesLabel.label};
}
});
}
}
}, //
}, // watch: {
mounted() {
retrieveAppDictionaries('ads_list', ['ads_per_page', 'categoriesLabels']); // REQUEST TO DB
bus.$on('appDictionariesRetrieved', (response) => {
if (response.request_key === 'ads_list') { // this is triggered AFTER watch
this.ads_per_page = response.ads_per_page
this.categoriesLabels = response.categoriesLabels
// this.$forceUpdate() // IF UNCOMMENT THAT DOES NOT HELP
Vue.$forceUpdate() // THAT DOES NOT HELP
}
})
this.loadAds(true)
}, // mounted() {
I found this Can you force Vue.js to reload/re-render?
branch and tried some decisions, like
Vue.$forceUpdate()
but that does not work.
If there is a right way to trigger watch defaultAdSavedFilters AFTER I read array from db ?
Modified BLOCK :
I use Vuex actions/mutations when I need to read / keep /use /update data of the logged user, like
defaultAdSavedFilters, which is defined as :
computed: {
defaultAdSavedFilters: function () {
return this.$store.getters.defaultAdSavedFilters
},
Data ads_per_page(used for pagionaion), categoriesLabels(used for selection input items) has nothing to do with
logged user, that is why I do not use vuex for them, and I use retrieveAppDictionaries method to read them from the db
and bus to listen to them, which is defined as :
import {bus} from '#/main'
Sure I have data( block :
export default {
data() {
return {
...
ads_per_page: 20,
categoriesLabels: [],
...
}
},
"vue": "^2.6.10",
"vue-router": "^3.1.3",
"vuex": "^3.1.2"
Thanks!

Please add the data() method from you component. But I'm pretty sure it is NOT triggering because of the way you are assigning the result from the API call.
Try this:
mounted() {
retrieveAppDictionaries('ads_list', ['ads_per_page', 'categoriesLabels']); // REQUEST TO DB
bus.$on('appDictionariesRetrieved', (response) => {
if (response.request_key === 'ads_list') { // this is triggered AFTER watch
this.ads_per_page = [ ...response.ads_per_page ]
this.categoriesLabels = [ ...response.categoriesLabels ]
}
})
this.loadAds(true)
}
However, I don't understand what bus is doing for you and why you are NOT using Vuex actions/mutations

Related

How to full state before going throw script in component vue

Mey be it is simple, but I'm new in frontend. I have a page component. And I need to fetch data before component calculated.
import {mapActions, mapGetters} from 'vuex'
export default {
name: "notFoundPage",
methods: {
...mapActions([
'GET_SUBCATEGORIES_FROM_CATEGORIES'
]),
},
computed: {
...mapGetters([
'SUBCATEGORIES'
]),
subCategories() {
// doing some calculations with already updated SUBCATEGORIES in store
}
return result;
}
},
created() {
this.GET_SUBCATEGORIES_FROM_CATEGORIES()
> **// from here we go to store**
},
mounted() {
this.GET_SUBCATEGORIES_FROM_CATEGORIES()
}
}
store:
let store = new Vuex.Store({
state: {
categories: [],
subcategories: []
},
mutations: {
SET_CATEGORIES_TO_STATE: (state, categories) => {
state.categories = categories;
},
SET_SUBCATEGORIES_TO_STATE: (state, subcategories) => {
state.subcategories = subcategories;
}
},
actions: {
GET_CATEGORIES_FROM_API({commit}) {
return axios('http://localhost:3000/categories',
{
method: "GET"
})
But here compiler returns to component. I do not have any idea, why it is not finishing this action. And after calculating the computed block in component it returns to this point. But I need 'SET_CATEGORIES_TO_STATE' already updated
.then((categories) => {
commit('SET_CATEGORIES_TO_STATE', categories.data)
return categories;
}).catch((error) => {
console.log(error);
return error;
})
},
GET_SUBCATEGORIES_FROM_CATEGORIES({commit}) {
this.dispatch('GET_CATEGORIES_FROM_API').then(categories => {
let subs = categories.data.map(function(category) {
return category.subcategories.map(function(subcategory) {
return subcategory.name
})
})
commit('SET_SUBCATEGORIES_TO_STATE', subs)
return subs
})
}
},
getters: {
CATEGORIES(state) {
return state.categories;
},
SUBCATEGORIES(state) {
return state.subcategories;
}
}
if you have difficulties with timings and async tasks, why don't you use async/await?
you want to wait in a async function (for example calling a backend for data) till the data is fetched. then you want to manipulate/delete/change/add, do what ever you want with that data and display the result on screen.
the point is, Vue is a reactive Framework, which means it rerenders (if the setup is correct made) the content by itself after what ever calculation is finished. so don't worry about something like that.
to be honest, the question is asked really weird. and your code is hard to read. sometimes moving two steps back and try a other way isn't false as well.

Redo the api call everytime I change the data value on VueJs

I'm trying to update a request of an axios.get
I have a method that adds 1 to the param data (the default value is 1), but even thought I'm updating the param value, the page won't change the content because it's not updating the get requisition
I know there something similar in react with componentDidUpdate method
Here's my code
Api request
async created() {
const {
data: {
data: { items, pagination },
},
} = await this.$axios.get(`/faq?page=${this.param}`)
},
Method:
methods: {
next() {
this.param = this.param + 1
},
},
So is it possible to redo the create() everytime i use the method next?
created() hook is called only once during a lifecycle, you can use watcher instead in order to listen to variable changes
watch: {
param: {
immediate: true,
handler(newVal, oldVal) {
if (newVal !== oldVal) {
await this.$axios.get(`/faq?page=${newVal}`)
}
}
}
}
For more info, please take a look at: https://v2.vuejs.org/v2/guide/computed.html#Computed-vs-Watched-Property

Setting intial local data from Vuex store giving "do not mutate" error

I thought I understood the correct way to load inital state data from Vuex into the local data of a component, but why is this giving me “[vuex] do not mutate vuex store state outside mutation handlers.” errors! I am using a mutation handler!
I want my component data to start empty, unless coming back from a certain page (then it should pull some values from Vuex).
The component is using v-model=“selected” on a bunch of checkboxes. Then I have the following:
// Template
<grid-leaders
v-if="selected.regions.length"
v-model="selected"
/>
// Script
export default {
data() {
return {
selectedProxy: {
regions: [],
parties: [],
},
}
},
computed: {
selected: {
get() {
return this.selectedProxy
},
set(newVal) {
this.selectedProxy = newVal
// If I remove this next line, it works fine.
this.$store.commit("SET_LEADER_REGIONS", newVal)
},
},
},
mounted() {
// Restore saved selections if coming back from a specific page
if (this.$store.state.referrer.name == "leaders-detail") {
this.selectedProxy = {...this.$store.state.leaderRegions }
}
}
}
// Store mutation
SET_LEADER_REGIONS(state, object) {
state.leaderRegions = object
}
OK I figured it out! The checkbox component (which I didn't write) was doing this:
updateRegion(region) {
const index = this.value.regions.indexOf(region)
if (index == -1) {
this.value.regions.push(region)
} else {
this.value.regions.splice(index, 1)
}
this.$emit("input", this.value)
},
The line this.value.regions.push(region) is the problem. You can't edit the this.value prop directly. I made it this:
updateRegion(region) {
const index = this.value.regions.indexOf(region)
let regions = [...this.value.regions]
if (index == -1) {
regions.push(region)
} else {
regions.splice(index, 1)
}
this.$emit("input", {
...this.value,
regions,
})
},
And then I needed this for my computed selected:
selected: {
get() {
return this.selectedProxy
},
set(newVal) {
// Important to spread here to avoid Vuex mutation errors
this.selectedProxy = { ...newVal }
this.$store.commit("SET_LEADER_REGIONS", { ...newVal })
},
},
And it works great now!
I think the issue is that you can't edit a v-model value directly, and also you also have to be aware of passing references to objects, and so the object spread operator is a real help.

How with vue-head plugin to set meta read from db?

Making vue/cli 4 app I want to add page head on data from database
I use https://github.com/ktquez/vue-head
But the problem is that title and meta are set with initial data of vars which I have
in data block, but not from the values of the var I read from db. I do :
...
import Vue from 'vue'
import VueRouter from 'vue-router'
import VueHead from 'vue-head'
Vue.use(VueHead)
Vue.use(VueRouter)
export default {
...
data() {
return {
...
site_name : 'QQWWEERRTTYY',
...
}
}, // data () {
head: {
title: function () {
console.log('-1 this.site_name::')
console.log(this.site_name) // In console I see “QQWWEERRTTYY” value, but not value what I read from db
return {
inner: 'AAA : '+this.site_name
}
},
meta: function () {
console.log('-2 this.site_name::')
console.log(this.site_name) // In console I see “QQWWEERRTTYY” value, but not value what I read from db
return [
{ name: 'description', content: 'BBB Events of '+ this.site_name }
]
},
},
...
mounted() {
retrieveAppDictionaries('eventsTimelinePage', ['site_name'])
bus.$on('appDictionariesRetrieved', (data) => {
if (data.request_key === 'eventsTimelinePage') {
this.site_name = data.site_name // I READ DATA FROM DB
}
})
}, // mounted() {
I know that WATCH can be used in similar case, when I need to catch event when value was assigned
to the site_name var, but I do not know to can I use Watch is this case?
"vue": "^2.6.10",
"vue-router": "^3.1.3",
"vue-head": "^2.2.0",
Thanks!
Based on this GitHub issue thread, you can now self.$emit('updateHead') to force vue-head to recalculate based on reactive data.

vuejs2: how can i destroy a watcher?

How can i destroy this watcher? I need it only one time in my child component, when my async data has loaded from the parent component.
export default {
...
watch: {
data: function(){
this.sortBy();
},
},
...
}
gregor ;)
If you construct a watcher dynamically by calling vm.$watch function, it returns a function that may be called at a later point in time to disable (remove) that particular watcher.
Don't put the watcher statically in the component, as in your code, but do something like:
created() {
var unwatch = this.$watch(....)
// now the watcher is watching and you can disable it
// by calling unwatch() somewhere else;
// you can store the unwatch function to a variable in the data
// or whatever suits you best
}
More thorough explanation may be found from here: https://codingexplained.com/coding/front-end/vue-js/adding-removing-watchers-dynamically
Here is an example:
<script>
export default {
data() {
return {
employee: {
teams: []
},
employeeTeamsWatcher: null,
};
},
created() {
this.employeeTeamsWatcher = this.$watch('employee.teams', (newVal, oldVal) => {
this.setActiveTeamTabName();
});
},
methods: {
setActiveTeamTabName() {
if (this.employee.teams.length) {
// once you got your desired condition satisfied then unwatch by calling:
this.employeeTeamsWatcher();
}
},
},
};
</script>
If you are using vue2 using the composition-api plugin or vue3, you can use WatchStopHandle which is returned by watch e.g.:
const x = ref(0);
setInterval(() => {
x.value++;
}, 1000);
const unwatch = watch(
() => x.value,
() => {
console.log(x.value);
x.value++;
// stop watch:
if (x.value > 3) unwatch();
}
);
For this kind of stuff, you can investigate the type declaration of the API, which is very helpful, just hover the mouse on it, and it will show you a hint about what you can do: