We're trying to add a boot file to Quasar Framwework to be able to use #vue/apollo-composable with the Vue composition API. This tutorial explains how that is done for the old apollo-client and this one for the new version.
The issue we're having is to connect the Apollo client to Vue. So we need to translate the example from the docs to a Quasar boot file:
// example docs
import { provide } from '#vue/composition-api'
import { DefaultApolloClient } from '#vue/apollo-composable'
const app = new Vue({
setup () {
provide(DefaultApolloClient, apolloClient)
},
render: h => h(App),
})
The Quasar boot file:
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { provide } from '#vue/composition-api'
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache
});
export default async ({ app } /* { app, router, Vue ... } */) => {
app.setup(provide(DefaultApolloClient, apolloClient))
}
The issue:
What is the correct syntax to use in the Quasar Framework boot file to add the Apollo client?
Found the correct syntax in this answer:
import { boot } from 'quasar/wrappers'
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { provide } from '#vue/composition-api'
import config from 'src/app-config.json'
export default boot(({ app }) => {
const httpLink = createHttpLink({
uri: config.resources.gatewayApi.uri,
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache,
})
app.setup = () => {
provide(DefaultApolloClient, apolloClient)
return {}
}
})
I translated #DarkLite1's code to be TypeScript compatible. So following is the file src/boot/vue-apollo-4.ts (don't forget to register it in quasar.conf.js):
<pre>
import { createHttpLink } from 'apollo-link-http'
import { InMemoryCache } from 'apollo-cache-inmemory'
import { ApolloClient } from 'apollo-client'
import { DefaultApolloClient } from '#vue/apollo-composable'
import { provide } from '#vue/composition-api'
import { boot } from 'quasar/wrappers'
const httpLink = createHttpLink({
uri: 'http://localhost:8080/v1/graphql'
})
const cache = new InMemoryCache()
const apolloClient = new ApolloClient({
link: httpLink,
cache
})
export default boot(({ app }) => {
app.setup = () => {
provide(DefaultApolloClient, apolloClient)
return {}
}
})
</pre>
try use this :
export default ({ app, Vue }) => {
Vue.use(VueApollo)
app.apolloProvider = apolloProvider
}
Related
I'm trying to implement a locale parameter into my axiosConfig file:
import axios from "axios";
const instance = axios.create({
baseURL: "http://10.0.2.2:8000/api",
timeout: 2000,
});
instance.defaults.headers.common["locale"] = "en";
export default instance;
On each screen I make my get and post calls on screens as such:
axiosConfig
.get("/someroute")
.then((response) => {
//console.log(response.data);
})
.catch((error) => {
console.log(error.message);
});
The above code works as intended. Now I want to pass a "locale" parameter into all of my axios calls. This parameter will come from app locale (i use i18next). When I implement it as below, it throws an invalid hook error.
import axios from "axios";
import { useTranslation } from "react-i18next";
const { i18n } = useTranslation();
const instance = axios.create({
baseURL: "http://10.0.2.2:8000/api",
timeout: 2000,
});
instance.defaults.headers.common["locale"] = i18n.language;
export default instance;
What would be the correct way to set the locale header in my configuration?
You are getting this error because a hook should be called in a React Component or inside another hook. See Rules of Hooks. And here is what you could do for example:
Transform the file where you are setting the axios instance to a hook:
import axios from "axios";
import { useTranslation } from "react-i18next";
const useAxiosInstance = ()=>{
const { i18n } = useTranslation();
const instance = axios.create({
baseURL: "http://10.0.2.2:8000/api",
timeout: 2000,
});
instance.defaults.headers.common["locale"] = i18n.language;
return instance;
}
export default useAxiosInstance;
You include the hook at the top of the file where you are using the axios config and use it as an example this way:
import {useEffect} from "react";
import useAxiosConfig from "./path";
const AxiosConsumer = ()=>{
const axiosConfig = useAxiosConfig();
useEffect(()=>{
axiosConfig
.get("/someroute")
.then((response) => {
//console.log(response.data);
})
.catch((error) => {
console.log(error.message);
});
},[axiosConfig]);
return <></>
}
I wanted to move my global custom directive into a separate folder and import it from a file, but I fail to do so in Vue3.
I got a:
Uncaught TypeError: Cannot read properties of undefined (reading 'directive')
Here are my files:
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import VueGtag from 'vue-gtag'
import '#/plugins/gtag'
createApp(App)
.use(
VueGtag,
{
config: { id: process.env.VUE_APP_GOOGLE_ANALYTICS_ID },
pageTrackerTemplate(to) {
return {
page_title: to.name,
page_path: to.path,
}
},
},
router
)
.use(router)
.mount('#app')
/plugin/gtag.js
import Vue from 'vue'
import { event } from 'vue-gtag'
const track = binding => () => {
event('click', {
event_category: binding.value.category,
event_label: binding.value.label,
value: 1,
})
}
Vue.directive('track', {
beforeMount(el, binding) {
const trackFn = track(binding)
el.addEventListener('click', trackFn)
el.trackFn = trackFn
},
unmounted(el) {
el.removeEventListener('click', el.trackFn)
},
})
I am aware, that my gtag.js with import Vue from 'vue' is so Vuejs2 and now it should be imported with { createApp }.
But I just don't know, how to make it in Vuejs3 with the directive?
EDIT:
Thanks #Leo for the solution:
plugins/gtag.js
import { event } from 'vue-gtag'
const track = binding => () => {
event('click', {
event_category: binding.value.category,
event_label: binding.value.label,
value: 1,
})
}
const TrackDirective = {
beforeMount(el, binding) {
const trackFn = track(binding)
el.addEventListener('click', trackFn)
el.trackFn = trackFn
},
unmounted(el) {
el.removeEventListener('click', el.trackFn)
},
}
export default TrackDirective
main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import VueGtag from 'vue-gtag'
import TrackDirective from '#/plugins/gtag'
createApp(App)
.directive('track', TrackDirective)
.use(
VueGtag,
{
config: { id: process.env.VUE_APP_GOOGLE_ANALYTICS_ID },
pageTrackerTemplate(to) {
return {
page_title: to.name,
page_path: to.path,
}
},
},
router
)
.use(router)
.mount('#app')
You need to use directive direct on app variable
example bellow:
gtag.js
const TrackDirective = {
beforeMount: (el, binding) => {
el.addEventListener('click', () => {
console.info('tracking')
})
}
}
export default TrackDirective
main.js
import TrackDirective from "./track";
const app = createApp(App)
app.directive('track', TrackDirective)
app.mount('#app')
some component
<template>
<div v-track>
click on this text
</div>
</template>
reference: https://vuejs.org/guide/reusability/custom-directives.html#introduction
I'm trying to create server with ApolloClient and GraphQL but got the following error:
SyntaxError: Named export 'ApolloClient' not found. The requested
module '#apollo/client' is a CommonJS module, which may not support
all module.exports as named exports.
my code looks like this:
import { ApolloClient, InMemoryCache, createHttpLink } from '#apollo/client'
const httpLink = createHttpLink({
uri: 'http://localhost:4000/graphql',
})
const createApolloClient = () => {
return new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
})
}
export default createApolloClient
I tried
import pkg from '#apollo/client'
const { ApolloClient, InMemoryCache, createHttpLink } = pkg
but it didn't help
Good news #davit-gelovani
I reached to import Apollo the way you need, just like that :
import { ApolloClient, InMemoryCache } from "#apollo/client/core/core.cjs";
import { HttpLink } from "#apollo/client/link/http/http.cjs";
on #apollo/client version 3.7.0 🚀
I know similar questions already present in stackoverflow, but I still can't understand how to solve this. I am having the warning(look in the title) in my console.
You can reproduce the warning by the following code
//index.js
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
import axios from 'axios';
const app = createApp(App)
app.__proto__.axios = axios
app.use(store)
app.mount("#app")
##App.vue
<template>
<div class="TodoList">
<p v-for="todo in todos" :key="todo.id">{{ todo.title }}</p>
</div>
</template>
<script>
export default {
mounted() {
this.$store.dispatch("fillItems");
},
computed: {
todos() {
return this.$store.getters.todos;
},
},
};
</script>
<style>
</style>
##store.js
import { createStore } from 'vuex';
export const store = createStore({
state: {
todos: []
},
getters: {
todos(state) {
return state.todos
}
},
mutations: {
FILL_ITEMS(state, payload) {
state.todos = payload
}
},
actions: {
fillItems({ commit }) {
this.axios
.get("https://jsonplaceholder.typicode.com/todos")
.then(res => commit('FILL_ITEMS', res.data))
}
}
})
You could add axios to app.config.globalProperties in order to access it inside any child component :
const app = createApp(App)
app.config.globalProperties.axios=axios
in child component use this.axios
but you couldn't access it inside the store context because this in the actions refers to the store instance, so you should import axios inside the store file and use it like :
import { createStore } from 'vuex';
import axios from 'axios';
export const store = createStore({
state: {
todos: []
},
getters: {
todos(state) {
return state.todos
}
},
mutations: {
FILL_ITEMS(state, payload) {
state.todos = payload
}
},
actions: {
fillItems({ commit }) {
axios
.get("https://jsonplaceholder.typicode.com/todos")
.then(res => commit('FILL_ITEMS', res.data))
}
}
})
or you could assign axios to the store instance (It's not recommended specially with typescript) :
const app = createApp(App)
store.axios = axios
app.use(store)
app.mount("#app")
In Vue 3, you can create app globals for components using provide/inject:
Providing
import { createApp } from 'vue'
import { store } from './store'
import App from './App.vue'
import axios from 'axios';
const app = createApp(App)
app.provide('axios', axios); // Providing to all components here
app.use(store)
app.mount("#app")
Injecting
In the options API:
export default {
inject: ['axios']; // injecting in a component that wants it
}
In the composition API:
const { inject } = Vue;
...
setup() {
const axios = inject('axios'); // injecting in a component that wants it
}
Edit:
I answered too fast (thanks #BoussadjraBrahim), you're not asking about components, but I'll leave that answer too. If you just want to use axios in a separate module, you can use it like any import:
import axios from 'axios';
and use axios instead of this.axios
I am new to VueJS and I have been trying to setup graphql with my vuejs but I can't seem to get it right.
Funny thing is there's no error on my console apart from a
[Vue warn]: A plugin must either be a function or an object with an "install" function. error.
And this only comes up when I instantiate vueapollo to the use method.
Here's my main.js file
import { createApp } from 'vue';
import ElementUI from 'element-plus';
import ApolloClient from 'apollo-client';
import { createHttpLink } from 'apollo-link-http';
import { InMemoryCache } from 'apollo-cache-inmemory';
import VueApollo from 'vue-apollo';
import App from './App.vue';
import router from './router';
import store from './store';
import './assets/style/theme/index.css';
// HTTP connection to the API
const httpLink = createHttpLink({
// You should use an absolute URL here
uri: 'http://localhost:4999/graphql',
});
// Cache implementation
const cache = new InMemoryCache();
// Create the apollo client
const apolloClient = new ApolloClient({
link: httpLink,
cache,
});
/* The provider holds the Apollo client instances
that can then be used by all the child components. */
// eslint-disable-next-line
const apolloProvider = new VueApollo({
defaultClient: apolloClient,
});
createApp(App)
.use(ElementUI)
.use(apolloClient)
.use(store)
.use(router)
.mount('#app');
I have a page file where presently, I want to reach the "Hello" endpoint I created earlier
<template>
<div>
<h1>Hello</h1>
<h1>{{ hello }}</h1>
</div>
</template>
<script>
import gql from 'graphql-tag';
export default {
data() {
return {
hello: '',
};
},
apollo: {
// Simple query that will update the 'hello' vue property
hello: gql`
query {
hello
}
`,
},
};
</script>
I also struggled with it a couple of weeks ago.
I don't remember how, but I got it to work.
I post my code here, in case it helps anyone in the future.
The difference between the question author and my code is how I pass the Apollo client to the create-app setup function. I don't know if it is correct, but it works.
PS: I am also using TypeScript on top of everything.
My apollo-client.ts file
import { ApolloError } from '#apollo/client';
import { ApolloClient, InMemoryCache, HttpLink } from '#apollo/client/core';
import { ErrorResponse } from '#apollo/client/link/error';
import { onError } from '#apollo/client/link/error';
import { logErrorMessages } from '#vue/apollo-util';
function getHeaders() {
const headers = {
'content-type': 'application/json',
};
/* const token = window.localStorage.getItem('apollo-token');
if (token) {
headers['Authorization'] = `Bearer ${token}`;
} */
return headers;
}
// Create an http link:
const httpLink = new HttpLink({
uri: 'http://localhost:3000/query',
fetch: (uri: RequestInfo, options: RequestInit) => {
options.headers = getHeaders();
return fetch(uri, options);
},
});
const errorLink = onError((error: ErrorResponse | ApolloError) => {
if (process.env.NODE_ENV !== 'production') {
logErrorMessages(error);
}
});
// Create the apollo client
export const apolloClient = new ApolloClient({
cache: new InMemoryCache(),
connectToDevTools: true,
link: errorLink.concat(httpLink),
});
My main.ts file:
import { apolloClient } from './apollo-client';
import { createApp, provide, h } from 'vue';
import { DefaultApolloClient } from '#vue/apollo-composable';
import { createApolloProvider } from '#vue/apollo-option';
import { loadFonts } from './plugins/webfontloader';
import App from './App.vue';
import router from './router';
import store from './store';
import vuetify from './plugins/vuetify';
// TODO: Auth
// import authPlugin from "./auth/authPlugin"
// Create a provider
const apolloProvider = createApolloProvider({
defaultClient: apolloClient,
});
loadFonts();
const app = createApp({
setup() {
provide(DefaultApolloClient, apolloClient);
},
render: () => h(App),
});
app.use(router).use(store).use(vuetify).use(apolloProvider).mount('#app');