feat(components): allow usage of gql in template
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
## Props
|
||||
|
||||
- `mutation`: GraphQL query (transformed by `graphql-tag`)
|
||||
- `mutation`: GraphQL query (transformed by `graphql-tag`) or a function that receives the `gql` tag as argument and should return the transformed query
|
||||
- `variables`: Object of GraphQL variables
|
||||
- `optimisticResponse`: See [optimistic UI](https://www.apollographql.com/docs/react/features/optimistic-ui.html)
|
||||
- `update`: See [updating cache after mutation](https://www.apollographql.com/docs/react/api/react-apollo.html#graphql-mutation-options-update)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## Props
|
||||
|
||||
- `query`: GraphQL query (transformed by `graphql-tag`)
|
||||
- `query`: GraphQL query (transformed by `graphql-tag`) or a function that receives the `gql` tag as argument and should return the transformed query
|
||||
- `variables`: Object of GraphQL variables
|
||||
- `fetchPolicy`: See [apollo fetchPolicy](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-fetchPolicy)
|
||||
- `pollInterval`: See [apollo pollInterval](https://www.apollographql.com/docs/react/basics/queries.html#graphql-config-options-pollInterval)
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
## Props
|
||||
|
||||
- `document`: GraphQL document that contains the subscription.
|
||||
- `document`: GraphQL document that contains the subscription or a function that receives the `gql` tag as argument and should return the transformed document.
|
||||
- `variables`: Object which will automatically update the subscription variables.
|
||||
- `updateQuery`: Function in which on can update the query result if needed.
|
||||
|
||||
@@ -4,34 +4,26 @@ Those are components just like any others. They take a GraphQL document in their
|
||||
|
||||
The benefit is that you can use those components in the template directly instead of using the `apollo` option of your component. In some cases you don't even need to add a script part at all in your `.vue`! This is all even more declarative.
|
||||
|
||||
Here is a quick example of an `ApolloQuery` in a template:
|
||||
Here is a quick example of an [ApolloQuery](./query.md) in a template:
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<div class="users-list">
|
||||
<!-- Apollo Query -->
|
||||
<ApolloQuery :query="require('@/graphql/users.gql')">
|
||||
<!-- The result will automatically updated -->
|
||||
<template slot-scope="{ result: { data, loading } }">
|
||||
<!-- Some content -->
|
||||
<div v-if="loading">Loading...</div>
|
||||
<ul v-else>
|
||||
<li v-for="user of data.users" class="user">
|
||||
{{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</ApolloQuery>
|
||||
</div>
|
||||
<!-- Apollo Query -->
|
||||
<ApolloQuery :query="/* some query */">
|
||||
<!-- The result will automatically updated -->
|
||||
<template slot-scope="{ result: { data, loading } }">
|
||||
<!-- Some content -->
|
||||
<div v-if="loading">Loading...</div>
|
||||
<ul v-else>
|
||||
<li v-for="user of data.users" class="user">
|
||||
{{ user.name }}
|
||||
</li>
|
||||
</ul>
|
||||
</template>
|
||||
</ApolloQuery>
|
||||
</template>
|
||||
|
||||
<!-- No need for script -->
|
||||
|
||||
<style scoped>
|
||||
.user {
|
||||
list-style: none;
|
||||
padding: 12px;
|
||||
color: blue;
|
||||
}
|
||||
</style>
|
||||
```
|
||||
|
||||
See [ApolloQuery](./query.md) to learn how to write GraphQL queries in the template.
|
||||
|
||||
@@ -6,18 +6,26 @@ Here is an example:
|
||||
|
||||
```vue
|
||||
<ApolloMutation
|
||||
:mutation="require('@/graphql/userLogin.gql')"
|
||||
:mutation="gql => gql`
|
||||
mutation DoStuff ($name: String!) {
|
||||
someWork (name: $name) {
|
||||
success
|
||||
timeSpent
|
||||
}
|
||||
}
|
||||
`"
|
||||
:variables="{
|
||||
email,
|
||||
password,
|
||||
name
|
||||
}"
|
||||
@done="onDone"
|
||||
>
|
||||
<template slot-scope="{ mutate, loading, error }">
|
||||
<template v-slot="{ mutate, loading, error }">
|
||||
<button :disabled="loading" @click="mutate()">Click me</button>
|
||||
<p v-if="error">An error occured: {{ error }}</p>
|
||||
</template>
|
||||
</ApolloMutation>
|
||||
```
|
||||
|
||||
See [API Reference](../../api/apollo-mutation.md).
|
||||
See [ApolloQuery](./query.md) to learn how to write GraphQL queries in the template.
|
||||
|
||||
See [API Reference](../../api/apollo-mutation.md) for all the available options.
|
||||
|
||||
+365
-21
@@ -1,28 +1,372 @@
|
||||
# ApolloQuery
|
||||
|
||||
You can use the `ApolloQuery` (or `apollo-query`) component to make watched Apollo queries directly in your template.
|
||||
You can use the `ApolloQuery` (or `apollo-query`) component to have watched Apollo queries directly in your template.
|
||||
After reading this page, see the [API Reference](../../api/apollo-query.md) for all the possible options.
|
||||
|
||||
Here is an example:
|
||||
## Query gql tag
|
||||
|
||||
This is the recommended way of using the `ApolloQuery` component. It uses the same syntax with the `gql` tag like in the other examples:
|
||||
|
||||
```vue
|
||||
<ApolloQuery
|
||||
:query="require('../graphql/HelloWorld.gql')"
|
||||
:variables="{ name }"
|
||||
>
|
||||
<template slot-scope="{ result: { loading, error, data } }">
|
||||
<!-- Loading -->
|
||||
<div v-if="loading" class="loading apollo">Loading...</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div v-else-if="error" class="error apollo">An error occured</div>
|
||||
|
||||
<!-- Result -->
|
||||
<div v-else-if="data" class="result apollo">{{ data.hello }}</div>
|
||||
|
||||
<!-- No result -->
|
||||
<div v-else class="no-result apollo">No result :(</div>
|
||||
</template>
|
||||
</ApolloQuery>
|
||||
<template>
|
||||
<ApolloQuery
|
||||
:query="gql => gql`
|
||||
query MyHelloQuery ($name: String!) {
|
||||
hello (name: $name)
|
||||
}
|
||||
`"
|
||||
:variables="{ name }"
|
||||
>
|
||||
<!-- TODO -->
|
||||
</ApolloQuery>
|
||||
</template>
|
||||
```
|
||||
|
||||
See [API Reference](../../api/apollo-query.md).
|
||||
We are passing a function to the `query` prop that gets the `gql` tag as argument, so we can write the GraphQL document directly.
|
||||
|
||||
The above example also features `variables` passed to the query using the prop with the same name.
|
||||
|
||||
Inside the default slot of `ApolloQuery`, you can access various slot data about the watched query, like the `result` object:
|
||||
|
||||
```vue
|
||||
<template v-slot="{ result: { loading, error, data } }">
|
||||
<!-- Loading -->
|
||||
<div v-if="loading" class="loading apollo">Loading...</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div v-else-if="error" class="error apollo">An error occured</div>
|
||||
|
||||
<!-- Result -->
|
||||
<div v-else-if="data" class="result apollo">{{ data.hello }}</div>
|
||||
|
||||
<!-- No result -->
|
||||
<div v-else class="no-result apollo">No result :(</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
Here is the complete example component:
|
||||
|
||||
```vue
|
||||
<script>
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
name: 'Anne'
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<input v-model="name" placeholder="Enter your name">
|
||||
|
||||
<ApolloQuery
|
||||
:query="gql => gql`
|
||||
query MyHelloQuery ($name: String!) {
|
||||
hello (name: $name)
|
||||
}
|
||||
`"
|
||||
:variables="{ name }"
|
||||
>
|
||||
<template v-slot="{ result: { loading, error, data } }">
|
||||
<!-- Loading -->
|
||||
<div v-if="loading" class="loading apollo">Loading...</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div v-else-if="error" class="error apollo">An error occured</div>
|
||||
|
||||
<!-- Result -->
|
||||
<div v-else-if="data" class="result apollo">{{ data.hello }}</div>
|
||||
|
||||
<!-- No result -->
|
||||
<div v-else class="no-result apollo">No result :(</div>
|
||||
</template>
|
||||
</ApolloQuery>
|
||||
</div>
|
||||
</template>
|
||||
```
|
||||
|
||||
If you are not using `vue-cli-plugin-apollo`, you need to configure [vue-loader](https://vue-loader.vuejs.org) to transpile the string template tag. `vue-loader` uses [Bublé](https://buble.surge.sh/guide/) under-the-hood to transpile code inside component templates. We need to add the `dangerousTaggedTemplateString` transform to Bublé for `gql` to work. For example, with Vue CLI:
|
||||
|
||||
```js
|
||||
// vue.config.js
|
||||
|
||||
module.exports = {
|
||||
chainWebpack: config => {
|
||||
config.module
|
||||
.rule('vue')
|
||||
.use('vue-loader')
|
||||
.loader('vue-loader')
|
||||
.tap(options => {
|
||||
options.transpileOptions = {
|
||||
transforms: {
|
||||
dangerousTaggedTemplateString: true,
|
||||
},
|
||||
}
|
||||
return options
|
||||
})
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
In a raw Webpack configuration, it would look like this:
|
||||
|
||||
```js
|
||||
module.exports = {
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.vue$/,
|
||||
use: [
|
||||
{
|
||||
loader: 'vue-loader',
|
||||
options: {
|
||||
transpileOptions: {
|
||||
transforms: {
|
||||
dangerousTaggedTemplateString: true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
/* Other rules ... */
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Query with gql files
|
||||
|
||||
An alternative way of using the component is by creating separate `.gql` files. Those files need pre-processing with [graphql-tag](https://github.com/apollographql/graphql-tag#webpack-preprocessing-with-graphql-tagloader).
|
||||
|
||||
```vue
|
||||
<template>
|
||||
<ApolloQuery
|
||||
:query="require('../graphql/HelloWorld.gql')"
|
||||
:variables="{ name }"
|
||||
>
|
||||
<template v-slot="{ result: { loading, error, data } }">
|
||||
<!-- Loading -->
|
||||
<div v-if="loading" class="loading apollo">Loading...</div>
|
||||
|
||||
<!-- Error -->
|
||||
<div v-else-if="error" class="error apollo">An error occured</div>
|
||||
|
||||
<!-- Result -->
|
||||
<div v-else-if="data" class="result apollo">{{ data.hello }}</div>
|
||||
|
||||
<!-- No result -->
|
||||
<div v-else class="no-result apollo">No result :(</div>
|
||||
</template>
|
||||
</ApolloQuery>
|
||||
</template>
|
||||
```
|
||||
|
||||
## Using fragments
|
||||
|
||||
Fragments are useful to share parts of GraphQL documents in other documents to retrieve the same data consistently and also to not copy-paste code.
|
||||
|
||||
Let's say we have this `GetMessages` query with a `messages` field that is an array of `Message` objects:
|
||||
|
||||
```gql
|
||||
query GetMessages {
|
||||
messages {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We want to extract all the fields in `messages` of the `Message` type into a fragment, so we can reuse it elsewhere.
|
||||
|
||||
First import the `gql` tag in the component:
|
||||
|
||||
```js
|
||||
import gql from 'graphql-tag'
|
||||
```
|
||||
|
||||
Then, inside the component definition, declare a new `fragments` object:
|
||||
|
||||
```js
|
||||
export default {
|
||||
fragments: {
|
||||
/** TODO */
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Here is what the `message` fragment, which is applied on the `Message` type, looks like:
|
||||
|
||||
```gql
|
||||
fragment message on Message {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
```
|
||||
|
||||
We can use the `gql` tag just like we do for queries:
|
||||
|
||||
```js
|
||||
export default {
|
||||
fragments: {
|
||||
message: gql`
|
||||
fragment message on Message {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Inside our component, we can now access the fragment with `this.$options.fragments.message`. To use the fragment in our `GetMessages` query, we need to use the GraphQL `...` spread operator and also put the fragment alongside the query:
|
||||
|
||||
```js
|
||||
gql`
|
||||
query GetMessages {
|
||||
messages {
|
||||
...message
|
||||
}
|
||||
}
|
||||
${$options.fragments.message}
|
||||
`
|
||||
```
|
||||
|
||||
Which will effectively produce this GraphQL document (that you can try on the GraphQL playground of your API):
|
||||
|
||||
```gql
|
||||
query GetMessages {
|
||||
messages {
|
||||
...message
|
||||
}
|
||||
}
|
||||
fragment message on Message {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
```
|
||||
|
||||
So what's happening here? GraphQL will find the `...` operator where we usually select fields in the `messages` field inside our query. The `...` operator is followed by the name of the fragment, `message`, which then looked up the whole GraphQL document. Here we have correctly defined the fragment, so it's found just under the query. Finally, GraphQL will copy all the fragment content and replace `...message` with it.
|
||||
|
||||
In the end, we obtain the final query:
|
||||
|
||||
```gql
|
||||
query GetMessages {
|
||||
messages {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
}
|
||||
fragment message on Message {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
```
|
||||
|
||||
Here is the full example component:
|
||||
|
||||
```vue
|
||||
<!-- MessageList.vue -->
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
fragments: {
|
||||
message: gql`
|
||||
fragment message on Message {
|
||||
id
|
||||
user {
|
||||
id
|
||||
name
|
||||
}
|
||||
text
|
||||
created
|
||||
}
|
||||
`
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<ApolloQuery
|
||||
:query="gql => gql`
|
||||
query GetMessages {
|
||||
messages {
|
||||
...message
|
||||
}
|
||||
}
|
||||
${$options.fragments.message}
|
||||
`"
|
||||
>
|
||||
<!-- Content... -->
|
||||
</ApolloQuery>
|
||||
</template>
|
||||
```
|
||||
|
||||
### Reusing the fragment
|
||||
|
||||
Now we can retrieve the `message` fragment in another component:
|
||||
|
||||
```vue
|
||||
<!-- MessageForm.vue -->
|
||||
<script>
|
||||
import gql from 'graphql-tag'
|
||||
import MessageList from './MessageList.vue'
|
||||
|
||||
export default {
|
||||
methods: {
|
||||
async sendMessage () {
|
||||
await this.$apollo.mutate({
|
||||
mutation: gql`
|
||||
mutation SendMessage ($input: SendMessageInput!) {
|
||||
addMessage (input: $input) {
|
||||
newMessage {
|
||||
...message
|
||||
}
|
||||
}
|
||||
}
|
||||
${MessageList.fragments.message}
|
||||
`,
|
||||
variables: {
|
||||
/* variables here */
|
||||
},
|
||||
/* other options here */
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
```
|
||||
@@ -12,8 +12,18 @@ Here is an example:
|
||||
<template>
|
||||
<ApolloQuery :query="...">
|
||||
<ApolloSubscribeToMore
|
||||
:document="require('../gql/MessageAdded.gql')"
|
||||
:variables="{ channel }"
|
||||
:document="gql => gql`
|
||||
subscription messageChanged ($channelId: ID!) {
|
||||
messageAdded (channelId: $channelId) {
|
||||
type
|
||||
message {
|
||||
id
|
||||
text
|
||||
}
|
||||
}
|
||||
}
|
||||
`"
|
||||
:variables="{ channelId }"
|
||||
:updateQuery="onMessageAdded"
|
||||
/>
|
||||
|
||||
@@ -44,7 +54,9 @@ export default {
|
||||
</script>
|
||||
```
|
||||
|
||||
See [API Reference](../../api/apollo-subscribe-to-more.md).
|
||||
See [ApolloQuery](./query.md) to learn how to write GraphQL queries in the template.
|
||||
|
||||
See [API Reference](../../api/apollo-subscribe-to-more.md) for all the available options.
|
||||
|
||||
## Examples of `updateQuery`
|
||||
|
||||
|
||||
@@ -115,3 +115,24 @@ new Vue({
|
||||
```
|
||||
|
||||
You are now ready to use Apollo in your components!
|
||||
|
||||
## IDE integration
|
||||
|
||||
### Visual Studio Code
|
||||
|
||||
If you are using VS Code, it's recommended to install the [Apollo GraphQL extension](https://marketplace.visualstudio.com/items?itemName=apollographql.vscode-apollo).
|
||||
|
||||
Then configure it by creating a `apollo.config.js` file in the root folder of the Vue project:
|
||||
|
||||
```js
|
||||
// apollo.config.js
|
||||
module.exports = {
|
||||
client: {
|
||||
service: {
|
||||
name: 'my-app',
|
||||
// URL to the GraphQL API
|
||||
url: 'http://localhost:3000/graphql',
|
||||
},
|
||||
},
|
||||
}
|
||||
```
|
||||
|
||||
+2
-1
@@ -36,7 +36,8 @@
|
||||
},
|
||||
"homepage": "https://github.com/Akryum/vue-apollo#readme",
|
||||
"peerDependencies": {
|
||||
"apollo-client": "^2.0.0"
|
||||
"apollo-client": "^2.0.0",
|
||||
"graphql-tag": "^2.10.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^2.4.1",
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import { addGqlError } from '../../lib/utils'
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
export default {
|
||||
props: {
|
||||
mutation: {
|
||||
type: Object,
|
||||
type: [Function, Object],
|
||||
required: true,
|
||||
},
|
||||
|
||||
@@ -49,8 +50,14 @@ export default {
|
||||
mutate (options) {
|
||||
this.loading = true
|
||||
this.error = null
|
||||
|
||||
let mutation = this.mutation
|
||||
if (typeof mutation === 'function') {
|
||||
mutation = mutation(gql)
|
||||
}
|
||||
|
||||
this.$apollo.mutate({
|
||||
mutation: this.mutation,
|
||||
mutation,
|
||||
client: this.clientId,
|
||||
variables: this.variables,
|
||||
optimisticResponse: this.optimisticResponse,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
function isDataFilled (data) {
|
||||
return Object.keys(data).length > 0
|
||||
}
|
||||
@@ -14,7 +16,7 @@ export default {
|
||||
|
||||
props: {
|
||||
query: {
|
||||
type: Object,
|
||||
type: [Function, Object],
|
||||
required: true,
|
||||
},
|
||||
|
||||
@@ -118,7 +120,12 @@ export default {
|
||||
|
||||
query () {
|
||||
return {
|
||||
query () { return this.query },
|
||||
query () {
|
||||
if (typeof this.query === 'function') {
|
||||
return this.query(gql)
|
||||
}
|
||||
return this.query
|
||||
},
|
||||
variables () { return this.variables },
|
||||
fetchPolicy: this.fetchPolicy,
|
||||
pollInterval: this.pollInterval,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import gql from 'graphql-tag'
|
||||
|
||||
let uid = 0
|
||||
|
||||
export default {
|
||||
@@ -10,7 +12,7 @@ export default {
|
||||
|
||||
props: {
|
||||
document: {
|
||||
type: Object,
|
||||
type: [Function, Object],
|
||||
required: true,
|
||||
},
|
||||
|
||||
@@ -52,8 +54,13 @@ export default {
|
||||
refresh () {
|
||||
this.destroy()
|
||||
|
||||
let document = this.document
|
||||
if (typeof document === 'function') {
|
||||
document = document(gql)
|
||||
}
|
||||
|
||||
this.$_sub = this.getDollarApollo().addSmartSubscription(this.$_key, {
|
||||
document: this.document,
|
||||
document,
|
||||
variables: this.variables,
|
||||
updateQuery: this.updateQuery,
|
||||
linkedQuery: this.getApolloQuery(),
|
||||
|
||||
Reference in New Issue
Block a user