I am working on a new project with vue and vue-apollo.
I have one component showing the user name (UserShow.vue):
<template>
<div v-if="!this.$apollo.queries.user.loading">
Your name is: {{user.firstname}}
</div>
</template>
<script>
import gql from "graphql-tag";
export default {
apollo: {
user: {
query: gql`{
user (id: 1) {
id
firstname
}
}`
}
}
}
</script>
and one component for editing (UserEdit.vue):
<template>
<div v-if="!this.$apollo.queries.user.loading">
Edit your name: <input v-model="user.firstname" />
</div>
</template>
<script>
import gql from "graphql-tag";
export default {
apollo: {
user: {
query: gql`{
user (id: 1) {
id
firstname
}
}`
}
}
}
</script>
Both components are shown on the same page, absolutely no mutations involved. just these two simple components.That's all
As soon as I change the name in the input field, the username is updated in the View.
That feels strange to me and I don't like it.
Apollo is doing the query and populating the vue data()
property of each component. But why is the data shared in some way? data
in a vue component should be private to this component as long as I don't pass it to a subcomponent (like with v-model in the input field).
If I look into my Apollo Dev Console into the Cache I can't see the change in firstname. So how does it work?
If I don't want this behavior I have only one strange option:
I can add another attribute to the query like so:
<template>
<div v-if="!this.$apollo.queries.user.loading">
Edit your name:
<input v-model="user.firstname" />
</div>
</template>
<script>
import userQuery from "./user";
import gql from "graphql-tag";
export default {
apollo: {
user: {
query: gql`{
user (id: 1) {
id
firstname
age // added to create a different query
}
}`
}
}
}
</script>
Adding "age" to the query changes the behavior. That's even stranger! How can I write reliable components with such a behavior? Or did I miss some crucial concept?
Source code can be found here: https://github.com/kicktipp/hello-apollo
Bug or feature?
I had a look at your code, and I'm pretty sure I know what's going on.
If you read through this thread you'll see that data returned from queries was initially intended by Apollo to be immutable (you shouldn't use it as a v-model
). In fact, it used to be frozen so attempts to use it as a data source resulted in broken reactivity.
Then this happened. Now the query results are mutable and you are mutating Apollo's internal data.
This explains why one update would immediately modify the display output of another component with an identical query.
Try something like this instead:
import userQuery from './user';
export default {
data() {
return { user: {} };
},
apollo: {
user: {
query: userQuery,
manual: true,
result({ data }) {
this.user = { ...data.user };
},
},
},
};
Note the following:
user
is initialized in data
manual: true
is set so user
isn't automatically set by Vue Apolloresult
we set this.user
using a spread (or Object.assign) to avoid copying a reference. If the object has more than one level, consider using something like cloneDeep.You'll need to make similar modifications in all places where you intend to mutate the query results.