Prevent graphql query in vue-apollo if input variable is null - vuejs2

I've been doing some reading and have come up with a query setup for a contact input field. I would like to avoid running this query at component startup with null input. I could manually run the queries through computed methods maybe, but is there a simple way to prevent this?
apollo: {
search: {
query: () => contactSearchGQL,
variables() {
return {
searchText: this.searchText,
};
},
debounce: 300,
update(data) {
console.log("received data: " + JSON.stringify(data));
},
result({ data, loading, networkStatus }) {
console.log("We got some result!")
},
error(error) {
console.error('We\'ve got an error!', error)
},
prefetch() {
console.log("contact search, in prefetch");
if ( this.searchText == null ) return false;
return true;
},
},
},
I think I'm not understanding something about prefetch, or if it's even applicable here?

You should utilize the skip option for that, as shown in the docs:
apollo: {
search: {
query: () => contactSearchGQL,
variables() {
return {
searchText: this.searchText,
};
},
skip() {
return !this.searchText;
},
...
},
},
Anytime searchText updates, skip will reevaluate -- if it evaluates to false, the query will be ran. You can also set the skip property directly if you need to control this logic elsewhere in your component:
this.$apollo.queries.search.skip = true
The prefetch option is specific to SSR. By default, vue-apollo will prefetch all queries in server-side rendered components. Setting prefetch to false disables this functionality for a specific query, which means that particular query won't run until the component is rendered on the client. It does not mean the query is skipped. See here for more details about SSR in vue-apollo.

Apollo Client
const { loading, error, data } = useQuery(GET_SOME_DATA_QUERY, {
variables: { param1 },
skip: param1 === '',
})
This query will be fired only when param1 is not empty.

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.

Vuex state changing with mutation - apollo graphql query

I have a form where the user edits the object - in my case, metadata about a story - after it is loaded from GraphQL. However, when I use my vue-router guard to check if the story has been changed, the story state is always the modified value.
Vuex story.js
...
getters: {
...formSubmit.getters,
getStory: (state) => {
return state.story},
getEditedStory: (state) => state.editedStory,
getStoryDescription: (state) => {
return state.story.description
}
},
mutations: {
...formSubmit.mutations,
setStory(state, payload) {
state.story = payload
},
setEditedStory(state, payload) {
state.editedStory = payload
}
},
...
Form component
export default {
...
apollo: {
story: {
query: gql`query GetStory($id: ID!) {
story(id: $id) {
name
summary
description
}
}`,
variables() {
return {
id: this.id
}
},
result({ data}) {
this.setText(data.story.description)
this.setStory(data.story)
this.setEditedStory(data.story)
},
}
},
...
In my form I have the values mapped with v-model:
<v-text-field
v-model="story.name"
class="mx-4"
label="Add your title..."
single-line
:counter="TITLE_TEXT_MAX_LENGTH"
outlined
solo
:disabled="updateOrLoadInProgress"
/>
However, for some reason whenever I call this.getStory its value is modified accordingly to the v-model. Why?
Although I still don't quite understand why, it seems like the changes to the apollo variable story affect the store.story values with using the mutations to set them.
I've modified the initial setters to be like this:
this.setText(data.story.description)
let loadedStory = {
name: data.story.name,
description: data.story.description,
summary: data.story.summary,
}
this.setStory(loadedStory)
this.setEditedStory(data.story)
},
which seems to prevent the state.story from following the changes to the apollo created story variable. This seems like unexpected behaviour, and I don't quite understand what part of javascript object assignment makes this work this way.

How to react once to a change in two or more watched properties in vuejs

There are many use cases for this but the one I'm dealing with is as follows. I have a page with a table, and two data properties, "page" and "filters". When either of these two variables are updated I need to fetch results from the server again.
However, there is no way as far as I can see to watch two variables and react only once, especially in the complicated instance updating filters should reset page to zero.
javascript
data: {
return {
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
watch: {
page (nv) {
this.fetchAPI()
},
filters: {
deep: true,
handler (nv) {
this.page = 0
this.fetchAPI()
}
}
},
methods: {
fetchAPI () {
// fetch data via axios here
}
}
If i update filters, its going to reset page to 0 and call fetchAPI() twice. However this seems like the most intuitive way to have a page with a table in it? filters should reset page to zero as you may be on page 500 and then your filters cause there to only be 1 page worth of results, and a change to either page or filters must call the api again.
Interested to see how others must be tackling this exact same problem reactively?
Take into the rule - watchers are the "last hope". You must not use them until you have other ways.
In your case, you could use events. This way the problem will go by itself:
Add #click="onPageChange" event to the page button (or whatever do you use).
Add #change="onFilterChange" event to the filter component (or whatever do you use). You can also use #click="onFilterChange" with some additional code to detect changes. Still, I am pretty sure you must have something like #change on the filter component.
Then your code will look like:
data: {
return {
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
methods: {
onPageChange () {
this.fetchAPI()
},
onFilterChange () {
this.page = 0
this.fetchAPI()
},
fetchAPI () {
// fetch data via axios here
}
}
In this case, the onFilterChange will change the page data but will not trigger the onPageChange method. So your problem will not exist.
Since the filters object is really complicated and has many options i have decided to keep it on a watcher that triggers setting page to zero and reloading the api. I now solve the problem as stated below. The 'pauseWatcher' data variable is a bit messy and needing to disable it in nextTick seems unideal but its a small price to pay for not having to manually hook up each and every filters input (some of them are far more complex than one input, like a date filter between two dates) and have them each emit onChange events that trigger reloading the api. It seems sad Vuejs doesnt have a lifecycle hook where you can access data properties and perhaps route paramters and make final changes to them before the watchers and computed properties turn on.
data: {
return {
pauseWatcher: true,
page: 0,
filters: {
searchText: '',
date: ''
}
}
},
watch: {
filters: {
deep: true,
handler (nv) {
if (this.pauseWatcher) {
return
}
this.page = 0
this.fetchAPI()
}
}
},
methods: {
fetchAPI () {
// fetch data via axios here
},
goToPage(page) {
this.page = page
this.fetchAPI()
},
decodeFilters () {
// decode filters from base64 URL string
}
},
created () {
this.pauseWatcher = true
this.page = Number(this.$route.query.page) || 0
this.filters = this.decodeFilters(this.$route.query.filters)
this.$nextTick(() => {
this.pauseWatcher = false
})
}

Understanding hooks in Sequelize (Returning vs. Attaching)

I am following my school's workshop regarding how to integrate Sequelize with Express. There is a section where we are learning to leverage hooks in our models—and in it I was confused by this:
Returning vs. Attaching
A hook runs with the instance of a Page being
saved given as an argument. We want to, therefore, attach a created
urlTitle to this page instance instead of returning it from the
function.
var Sequelize = require('sequelize');
var db = new Sequelize('postgres://localhost:5432/__wikistack__', {
logging: false,
});
const Page = db.define(
'page',
{
title: {
type: Sequelize.STRING,
},
urlTitle: {
type: Sequelize.STRING,
},
content: {
type: Sequelize.TEXT,
},
status: {
type: Sequelize.ENUM('open', 'closed'),
},
},
{
hooks: {
beforeValidate: function(page) {
if (page.title) {
// Removes all non-alphanumeric characters from title
// And make whitespace underscore
return (page.urlTitle = page.title.replace(/\s/g, '_').replace(/\W/g, ''));
} else {
// Generates random 5 letter string
return (urlTitle = Math.random()
.toString(36)
.substring(2, 7));
}
},
},
}
);
Can someone explain this? How can the function in the hook not return something? The above works, so the hook/function is returning something.
Thanks in advance!
Hooks are just code that gets run at certain life cycle points of a record instance. You can have them be purely side effects. In your case, all you need to do is modify the page object that the hook is passed, return doesn't help or hurt.
However, the return value of a hook is not useless. If you need to do anything async inside a hook, you have to return a promise.

Using Vue-Resource in Vuex store; getting maximum call stack size error

I'm trying to pull an array from my api to a component using vuex but I'm at a loss and probably in over my head in attempting this. With the api being accessed directly from a component I had it set up like this and it was fine:
data () {
return {
catalog:[],
}
},
created() {
this.$http.get('https://example.net/api.json').then(data => {
this.catalog = data.body[0].threads;
})
}
For reference the json looks similar to this:
[{
"threads": [{
"no: 12345,
"comment": "comment here",
"name": "user"
}, {
"no: 67890,
"comment": "another comment here",
"name": "user2"
}],
//this goes on for about 15 objects more in the array
"page": 0
}]
When I move this all to store I'm losing grasp on how to actually make this work. I've used vuex before just never with vue-resource.
//store.js
state: {
catalog: []
},
actions: {
getCatalog({commit}){
Vue.http.get('https://example.net/api.json').then(response => {
commit('LOAD_CATALOG', response.data.data)
});
}
},
mutations: {
LOAD_CATALOG (state) {
state.catalog.push(state.catalog)
}
},
getters: {
catalog: state => state.catalog,
}
//component.vue
created () {
this.$store.dispatch('getCatalog')
},
computed: {
catalog () {
return this.$store.getters.catalog
}
}
I'm aware this is wrong and I'm getting max call stack size errors. How can I get the same results as posted in the example above (this.catalog = data.body[0].threads;) when I put everything in store?
Let me know if anything needs clarification! I'm still pretty new at using Vue 2.0.
Your main issue is with your mutation.
Mutations are synchronous updates to the state, therefore you are correctly calling it from the action (where you process your async request) but you aren't passing the mutation anything to place in the state. Mutations accept arguments, so your LOAD_CATALOG mutation would accept the catalogData, i.e.
mutations: {
LOAD_CATALOG (state, catalogData) {
state.catalog = catalogData
}
},
Also if you are using vue resource for Vue 2 then you should be passing the body of the response to the mutation, i.e.
getCatalog({commit}){
Vue.http.get('https://example.net/api.json').then(response => {
commit('LOAD_CATALOG', response.body[0].threads)
});
}
The next issue is you don't need the getter, getters allow us to compute a derived state, you don't need them just to return an existing state (in your case catalog). A basic example of where you may use a getter would be to add 1 to a counter stored in state, i.e.
getters: {
counterAdded: state => state.counter + 1,
}
Once you've made these changes things will look a bit more like below:
//store.js
state: {
catalog: []
},
actions: {
getCatalog({commit}){
Vue.http.get('https://example.net/api.json').then(response => {
commit('LOAD_CATALOG', response.body[0].threads)
});
}
},
mutations: {
LOAD_CATALOG (state, catalogData) {
state.catalog = catalogData
}
},
//component.vue
created () {
this.$store.dispatch('getCatalog')
},
computed: {
catalog () {
return this.$store.state.catalog
}
}