Rename page with Apollo & Vue router - vue.js

I'd like to rename the title of the pages depending on the data received by Apollo.
For example, at the url myproject.com/media/title1, my GraphQL reply will be:
{
"data": {
"creation":
{
"id": "1",
"title": "Title1",
"creationDate": "2019-09-22 07:37:57 UTC",
"coverUrl": "linktomedia.com/image.jpg",
}
}
}
I want to get the title in data -> creation -> title as my title page name.
How can I do this ?
Thank you.

vue-apollo smart queries provide a result callback that's called when the query completes. You can complete any actions that depend on the data, including changing the title, inside the callback. Something like:
apollo: {
creation: {
query: YOUR_QUERY,
result: ({ data }) => {
document.title = data.creation.title
},
}
}

Related

Dynamically updating GraphQL Apollo variable causes page refresh

I am trying to dynamically fetch & filter data from API to a component in Vue using Apollo and GraplhQL. However, when the category value (this.category) is updated via user action (#click event), it causes the entire page to refresh.
Component code:
apollo: {
cards: {
prefetch: true,
query: cardsQuery,
variables() {
return {
category: this.category,
}
}
}
},
methods: {
categorySelector(category) {
this.category = category;
},
},
gql query:
query cards ($section: String){
cards (
where: { category: {section: $section} }
) {
name
category {
section
}
}
}
Am I doing something wrong with my graphql / apollo query or is the issue somewhere else?

Does Vue Apollo saves queries in cache?

I am currently trying to force Vue Apollo to query the latest data on an event. However, when the apollo query re-query on an event (i.e. on route change) it still loads old results (although the data had changed in the meanwhile). Only when I refresh/reload the webpage the query is fetching correctly and the newest data appears. Is it because the previous query results are saved in the cache? And if so, how do I pass options in my query: I read in the documentation that you can pass an option fetchPolicy: no-cache (see https://v4.apollo.vuejs.org/api/use-query.html#parameters). Where do I pass this option in my query? I have my query in dedicated files, for example my query is in a separated get_questionnaire.gql file like:
query ($username: String!, $id: ID!){
users(where: { username: $username }) {
Questionnaire(where: { id: $id }) {
OpenQuestions
ClosedQuestions
}
}
}
And I call it from my application:
import GetQuestionnaire from './graphql/get_questionnaire.gql';
await this.$apollo.query({
query: GetQuestionnaire,
variables: {
username: "Kilian",
id: "Project 1"
}
}).then((data) => {
...
}
Where do I define my options here? I tried the following but it doesn't work:
import GetQuestionnaire from './graphql/get_questionnaire.gql';
await this.$apollo.query({
query: GetQuestionnaire,
variables: {
username: "Kilian",
id: "Project 1"
},
options: {'fetchPolicy': 'network-only'} // I added this line
}).then((data) => {
...
}
So, would this "not caching" solve my problem? My main goal is to re-run my queries once my application has rendered but at the moment the responses are still showing me old data (although the data has changed in the meanwhile).
Many thanks for your help and ideas.
Indeed, Apollo saves queries as default in the cache. That is why a second query will response the previous cached response. My approach above is almost correct:
import GetQuestionnaire from './graphql/get_questionnaire.gql';
await this.$apollo.query({
query: GetQuestionnaire,
variables: {
username: "Kilian",
id: "Project 1"
},
options: {'fetchPolicy': 'network-only'} // this was wrong
}).then((data) => {
...
}
The correct way would be:
import GetQuestionnaire from './graphql/get_questionnaire.gql';
await this.$apollo.query({
query: GetQuestionnaire,
variables: {
username: "Kilian",
id: "Project 1"
},
fetchPolicy: 'network-only' // I changed this line
}).then((data) => {
...
}

Apollo smart query data not updating when variable is changed (Shopify Storefront API + Nuxt + Vuex + Apollo + GraphQL)

After days crawling through documentation I am unable to get past this issue.
I am building a currency toggle which will allow customers to switch between $AUD and $USD currencies in the UI. I am attempting to implement this via Shopify's Storefront API International Pricing GraphQL queries, specifically the #inContext directive.
I'm storing a countryCode variable in the Vuex store (which is updated via a toggle in the UI) and passing it into an Apollo Smart Query as a reactive variable in the page component.
I am able to successfully retrieve product data/prices for a certain country on first load but my issue is when updating the countryCode Smart Query variable via a Vuex action it has no effect on the Apollo data even though the Vuex data updates as desired.
I'm not sure if this issue is directly related to the Shopify Storefront API or a mistake with my Apollo query?
FYI – when updating the reactive variable productHandle, the data automatically updates as expected.
Page template
export default {
data: () => ({
productHandle: "product-name",
}),
// Get selected country code from Vuex store module
computed: {
currentCountryCode() {
return this.$store.getters["currency/countryCode"];
},
},
// Pass country code to Smart query variable
apollo: {
product: {
client: "shopify",
query: ProductByHandle,
variables: function () {
return {
handle: this.productHandle,
countryCode: this.currentCountryCode, // this should be reactive but doesn't effect query when variable is updated/changes
};
},
update(data) {
return _get(data, "productByHandle", {});
},
deep: true, // has no noticeable effect
},
},
}
GraphQL query (for context)
query ProductByHandle($handle: String!, $countryCode: CountryCode!) #inContext(country: $countryCode) {
productByHandle(handle: $handle) {
id
title
handle
productType
tags
description
descriptionHtml
availableForSale
vendor
images(first: 10) {
edges {
node {
transformedSrc
}
}
}
priceRange {
minVariantPrice{
amount
currencyCode
}
}
}
}
Any guidance or help would be greatly appreciated!
I've been finding that I have to set fetchPolicy: 'no-cache' on the Apollo query if I'm passing the product data to a component. If you're using the product data where the Apollo Query happens (i.e. same component / page ), you can skip the fetchPolicy and just add the watcher.
apollo: {
product: {
client: "shopify",
query: ProductByHandle,
fetchPolicy: 'no-cache',
variables: function () {
return {
handle: this.productHandle,
countryCode: this.currentCountryCode
}
}
}
}
Next, I've been using a watcher and call a method to update the pricing. For example:
methods: {
updatePrice(product) {
this.minPrice = Number(product.node.priceRange.minVariantPrice.amount).toFixed(2),
this.maxPrice = Number(product.node.priceRange.maxVariantPrice.amount).toFixed(2),
this.compareAtMinPrice = Number(product.node.compareAtPriceRange.minVariantPrice.amount).toFixed(2),
this.compareAtMaxPrice = Number(product.node.compareAtPriceRange.maxVariantPrice.amount).toFixed(2)
}
},
watch: {
product: function(newval, oldval) {
this.updatePrice(newval)
}
}
This works, but I'd love to figure out a solution that includes having caching. I suspect using a mutation when the currency changes might do it, but haven't dug into it.

How do I handle deletes in Vue Apollo with deeply nested queries?

I have a query that returns multiple nested objects to render a screen full of information. I want to delete one of the deeply-nested objects and update the screen optimistically (i.e. without running the complete query).
To explain the query and UI, I'll use a Trello board -like query as an example:
query everything {
getBoard(id: "foo") {
name
cardLists {
edges {
node {
id
name
cards {
edges {
node {
id
name
}
}
}
}
}
}
}
}
The result of this query is used to build a UI like this: https://d3uepj124s5rcx.cloudfront.net/items/170a0V1j0u0S2p1l290I/Board.png
I'm building the app using:
VueJS
Vue Apollo
Scaphold.io as my GraphQL store
When I want to delete one of the cards, I call:
deleteCard: function () {
this.$apollo.mutate({
mutation: gql`
mutation deleteCard($card: DeleteCardInput!) {
deleteCard(input: $card) {
changedCard {
id
}
}
}
`,
variables: {
'card': {
id: this.id
}
},
})
.then(data => {
// Success
})
}
The mutation is successfully deleting the card, but I want to update the UI immediately based on the mutation. The simple option for doing this is to call refetchQueries: ['everything'] — but this is an expensive query and too slow for quick UI updates.
What I want to do is update the UI optimistically, but the example mutations for Vue Apollo don't address either deletes or the deeply-nested scenario.
What is the right solution / best-practices for deleting an item from a deeply-nested query, and optimistically updating the UI?
If you look at the documentation for the optimistic Response of apollo you see that the optimistic response "fakes" the mutation result.
So it will handle the updateQueries function with the optimistic values. In your case this means that if you add the optimisticResponse property it should alter the query inside the apollo store with the optimistic values:
Could look like this:
this.$apollo.mutate({
mutation: gql `
mutation deleteCard($card: DeleteCardInput!) {
deleteCard(input: $card) {
changedCard {
id
}
}
}
`,
variables: {
'card': {
id: this.id
}
},
updateQueries: {
// .... update Board
},
optimisticResponse: {
__typename: 'Mutation',
deleteCard: {
__typename: 'Card', // graphQL type of the card
id: this.id,
},
},
})

Ember Data: working with API that returns larger payload on details endpoint than list endpoint

I'm using Ember with Ember Data and working with a REST API that returns only a small set of properties at the list endpoint and the full payload at the details endpoint. For example:
URL: /api/users
{
"users":[
{"id":1,"name":"Bob"},
{"id":2,"name":"Sam"}
]
}
URL: /api/users/1
{
"user":{
"id":1,
"name": "Bob",
"description": "Lorem ipsum dolor"
}
}
Notice how /api/users/1 returns more properties than the list. With Ember Data, how do you get it to fetch the full payload when necessary if I have my Routes set up as follows?
App.UsersRoute = Ember.Route.extend({
model: function () {
return this.store.find('user');
}
});
App.UserRoute = Ember.Route.extend({
model: function (params) {
return this.store.find('user', params.user_id);
}
});
The problem is this line:
this.store.find('user', params.user_id)
It doensn't request the full payload from the server if the list has already be loaded, so calling the store's find method returns the user with limited properties from its cache.
You could do something like:
var user = this.store.getById('user', params.user_id);
if (user) {
return user.reload();
} else {
return this.store.find('user', params.user_id);
}
Check store methods (getById, hasRecordForId, recordIsLoaded) and model reload.