Vue Apollo subscription not updating the query - vue.js

It's my first time implementing a GraphQL subscription, so excuse me if it's an obvious question.
I have a subscription which is working with Simple Subscription approach, but doesn't work with SubscribeToMore Here are both calls.
Simple Subscription:
$subscribe: {
onTransactionChanged: {
query: ON_TRANSACTION_CHANGED,
variables() {
return {
profileId: this.profile.profileId,
};
},
skip() {
return !this.profile?.profileId;
},
result({ data }: any) {
console.log(data.onTransactionChanged);
},
},
},
In this case, I see a new transaction in the console.
SubscribeToMore:
cartProducts: {
query: GET_CART_PRODUCTS,
loadingKey: "loading",
subscribeToMore: {
document: ON_TRANSACTION_CHANGED,
},
updateQuery: (previousResult: any, { subscriptionData }: any) => {
console.log("previousResult:", previousResult);
console.log("subscriptionData:", subscriptionData);
},
variables() {
return {
profileId: this.profile.profileId,
};
},
skip() {
return !this.profile?.profileId;
},
}
In this case, there is nothing in the console.
What am I doing wrong? Thank you in advance.

I just missed the curly braces scope. It's working
cartProducts: {
query: GET_CART_PRODUCTS,
loadingKey: "loading",
subscribeToMore: {
document: ON_TRANSACTION_CHANGED,
updateQuery(previousResult: any, { subscriptionData }: any) {
console.log("previousResult:", previousResult);
},
variables() {
return {
profileId: this.profile.profileId,
};
},
skip() {
return !this.profile?.profileId;
},
}
}

Related

Add a Class if element is in viewport vuejs nuxt

I want give an element a class when it's in the viewport and I don't know how I can handle it. I watched a few Videos on Youtube but nothing helped me.
I work with a boilerplate so I can't implement additional plugins.
My script looks actually like this.
export default {
head() {
return {
title: this.$t("headData.index.title"),
meta: [
{
hid: "description",
name: "description",
content: this.$t("headData.index.description")
}
]
}
},
data() {
return {
debugSingleScroll: true,
}
},
methods: {
handleScroll() {
if(this.debugSingleScroll) {
this.scrollToNextModule()
this.scrollToLastModule()
this.debugSingleScroll = false;
}
},
scrollToNextModule() {
this.$refs.factsModule.triggerAnimation();
},
scrollToLastModule() {
},
},
mounted () {
window.addEventListener('scroll', this.handleScroll);
},
destroyed () {
window.removeEventListener('scroll', this.handleScroll);
},
}

About Vue. How to encapsulate Axios to reduce request code redundancy

Now I'm writing a project using Vue.I used a lot of Axios requests,
How to encapsulate the request code to reduce redundancy.
getProvinces() {
this.axios
.get(this.gv.serverUrl + "/location/province/list")
.then((res) => {
this.location.province.provinces = res.data.data;
});
},
getCities() {
this.axios
.get(this.gv.serverUrl + "/location/city/list", {
params: {
pid: this.location.province.province,
},
})
.then((res) => {
this.location.city.cities = res.data.data;
});
},
getCountries() {
this.axios
.get(this.gv.serverUrl + "/location/country/list", {
params: {
cid: this.location.city.city,
},
})
.then((res) => {
this.location.country.countries = res.data.data;
});
},
Use Axios.all to do concurrent requests. That will help you to encapsulate status of all requests.
Not exactly but something like this given below:
let endpoints = [
'https://this.gv.serverUrl + "/location/province/list"',
'https://this.gv.serverUrl + "/location/city/list"',
'https://api.github.com/users/ejirocodes/followers',
'https://api.github.com/users/ejirocodes/following'
];
axios.all(endpoints.map((endpoint) => axios.get(endpoint))).then(
(data) => console.log(data),
)
Here is the link for more help and good explanation: https://blog.logrocket.com/using-axios-all-make-concurrent-requests/
You could create a method that makes the axios call, passing the path and the params (as an optional argument). So a method that could work for the code you are providing could be:
fetch(resource, params) {
return this.axios.get(this.gv.serverUrl + `/location/${resource}/list`, { params })
This fetch method would return a promise, and your methods would look like this:
getProvinces() {
this.fetch("/location/province/list")
.then((res) => {
this.location.province.provinces = res.data.data;
});
},
getCities() {
this.fetch("/location/city/list", { pid: this.location.province.province })
.then((res) => {
this.location.city.cities = res.data.data;
});
},
getCountries() {
this.fetch("/location/country/list", { cid: this.location.city.city})
.then((res) => {
this.location.country.countries = res.data.data;
});
},
A further refactor could be conducted if there was some kind of uniformity in the data property of the Vue instance.
I cannot see the shape of your data property, but from what I see it looks something like this:
data() {
return {
location: {
province: {
provinces: [...],
},
city: {
cities: [...],
},
country: {
countries: [...],
},
}
}
}
If you could change it to something like this:
data() {
return {
location: {
province: {
list: [...],
},
city: {
list: [...],
},
country: {
list: [...],
},
}
}
Then the refactor of the methods could be this:
fetch(resource, params) {
return this.axios
.get(this.gv.serverUrl + `/location/${resource}/list`, { params })
.then((res) => {
this.location[resource].list = res.data.data;
})
getProvinces() {
this.fetch("/location/province/list")
},
getCities() {
this.fetch("/location/city/list", { pid: this.location.province.province })
},
getCountries() {
this.fetch("/location/country/list", { cid: this.location.city.city})
},

How to pass values from state in vuex post request

I am collecting data using get and set for my form. I want to post the states to the api.
How can I move the states or group them somehow so I can pass them as to action?
state: {
firstname: "",
lastname: "",
},
mutations: {
setFirstName(state, value) {
state.firstname = value
},
setLastName(state, value) {
state.lastname = value
},
So it looks like this:
sendInfo({commit}, object) {
axios.post('API_URL', object)
.then((response) => {
...
})
}
computed: {
firstname: {
get() {
return this.$store.state.firstname
},
set(value) {
this.$store.commit("setFirstName", value)
}
},
or am I approaching this wrongly?
It's probably best to put these values inside a state object like:
state: {
user: {
firstname: '',
lastname: ''
}
}
You can set the object in an action
actions: {
setData({ commit }, payload) {
commit('SET_DATA', payload);
}
},
mutations: {
SET_DATA(state, payload) {
state.user = payload;
}
}
It also makes it concise when using mapState:
computed: {
...mapState(['user'])
}

Child element not updating props after change in data coming from apollo in nuxt/vue

UPDATE: the main issue seems to be that the props only get updated once. They should change when this.campaign.name becomes available.
I want to dynamically update the title and breadcrumb data fields and show them on the page. Currently page page shows undefined or null. How can I fix this?
I tried to create a computed value but it only seems to update once (after head and breadcrumb data is already showed). A method does not work since I don't have anything to trigger the method.
What is the correct way to fix this?
I am using nuxt generate to deploy the app.
export default {
components: { PageHeader },
middleware: 'authenticated',
data() {
return {
title: 'Campaigns' + this.campaignName,
breadcrumb: [
{
text: 'Campaigns',
href: '/'
},
{
text: this.campaignName,
href: '/'
}
],
campaign: ''
}
},
apollo: {
campaign: {
prefetch: true,
query: campaignQuery,
variables() {
return { id: this.$route.params.id }
}
}
},
computed: {
campaignName() {
return this.campaign && this.campaign.name
}
},
head() {
return {
title: this.title
}
}
}
</script>
Your computed property campaignName returns undefined cuz this.campaign.name is not defined
campaignName() {
if(this.campaign && this.campaign.name) return "Campaigns" + this.campaign.name;
return "default value";
}
Then you can use it directly in head
head() {
return {
title: this.campaignName
}
}
The solution was putting the data elements directly as a computer property. (so no recalculation)
export default {
components: { PageHeader },
middleware: 'authenticated',
data() {
return {}
},
apollo: {
campaign: {
prefetch: true,
query: campaignQuery,
variables() {
return { id: this.$route.params.id }
}
}
},
computed: {
title() {
return this.campaign && `Campaign: ${this.campaign.name}`
},
breadcrumb() {
return [
{
text: 'Campaign',
href: '/'
},
{
text: this.campaign && this.campaign.name,
href: '/'
}
]
}
},
head() {
return {
title: this.title
}
}
}
</script>

Vue vuex state watch is not working on first load

I have below code to get new ID using watch. Its working fine. However, I need the function to get the ID on first load as well, which is not working. Is there anything I am missing in my code?
watch: {
id: {
immediate: true,
handler(newV, oldV) {
this.id = newV;
},
},
},
mounted () {
store.watch(store.getters.getId, id => {
this.id = id;
});
},
created() {
this.userID();
},
methods: {
userID () {
console.log('this.id);
}
}
}
You can just do this:
data() {
return {id: null}
}
watch: {
'$store.getters.getId': {
immediate: true,
handler: function(newV) {
this.id = newV
},
},
}
Using a computed property is better, as your component does not 'own the id'.
computed: {
id() {
return this.$store.getters.getId
},
},