Error: vue-composition-api No vue dependency found - vue.js

I am having an issue with vue-composition-api.
I have included it in my main.ts as an include like this:
import "./registerServiceWorker";
import Vue from "vue";
import "./_core/plugins/composition-api";
import "./_core/plugins/global";
import "./_core/plugins/cloudinary";
import "./_core/plugins/gtm";
import { provide } from "#vue/composition-api";
import { ApolloClients } from "#vue/apollo-composable";
import vuetify from "./_core/plugins/veutify";
import contentfulClient from "./_core/plugins/vue-apollo-contentful";
import apiClient from "./_core/plugins/vue-apollo-api";
import VAnimateCss from "v-animate-css";
import App from "./app.component.vue";
import router from "./router";
import store from "./store";
Vue.config.productionTip = false;
import Meta from "vue-meta";
Vue.filter("formatPrice", function (value: string | number) {
if (typeof value !== "number") {
return value;
}
const formatter = new Intl.NumberFormat("en-GB", {
style: "currency",
currency: "GBP",
minimumFractionDigits: 2,
});
return formatter.format(value);
});
import "./scss/_core.scss";
Vue.filter("time", function (value) {
const date = new Date(value);
const options = {
timeZoneName: "short",
hour: "2-digit",
minute: "2-digit",
};
// eslint-disable-next-line
return date.toLocaleTimeString("en-GB", <any>options);
});
Vue.use(Meta, {
keyName: "head",
});
Vue.use(VAnimateCss);
new Vue({
router,
store,
vuetify,
setup() {
provide(ApolloClients, {
default: apiClient,
apiClient,
contentfulClient,
});
},
render: (h) => h(App),
}).$mount("#app");
compoistion-api.ts looks like this:
import Vue from "vue";
import vueCompositionApi from "#vue/composition-api";
Vue.use(vueCompositionApi);
This compiles, but in my application I see this error:
When digging deeper, I can see that it's complainling about my graph-query.ts script, which is imported in my get-metadata.ts script and in-turn is imported in app.component.ts.
I thought the main.ts file is imported first, so it should use composition-api before the app is mounted? Am I missing something here?
Let me know if you need more information

For anyone stumbling upon this: it might be because the composition API plugin is not initialized before using the API.
From a related github issue:
I had the same issue, it's because you are declaring any reactive elements (ref() / reactive()) before the plugin is installed in your Vue instance.
You can fixing by calling Vue.use(VueCompositionAPI) before importing any of your components/store

Related

Property 'axios' does not exist on type

It's my first time trying to use Vue and I've run into some troubles with axios. I have installed and imported it as per instructions on the website.
But when I'm trying to use it, I'm getting an error
TS2339: Property 'axios' does not exist on type 'TodoList'.
Here's my main.ts
import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios'
import VueAxios from 'vue-axios'
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
createApp(App).use(VueAxios, axios).use(ElementPlus).mount('#app')
And the TodoList with which I have a problem
<template>
<h1>Todo List</h1>
<todo-form #send-message="createTodo"></todo-form>
</template>
<script lang="ts">
import { ElMessage } from 'element-plus';
import { Options, Vue } from 'vue-class-component';
import TodoForm from './TodoForm.vue'
#Options({
components: {
TodoForm,
}
})
export default class TodoList extends Vue {
todos = [];
async mounted() {
await this.loadTodos();
}
async loadTodos() {
const response = await this.axios.get('http://localhost:8080/');
this.todos = response.data;
}
async createTodo(todo: any) {
console.log("Todo", todo)
ElMessage({
message: "Todo Created",
type: "success"
})
}
}
</script>
Any help is appreciated
It turned out that axios should be installed directly in application folder. Problem solved :)

Vuejs 3 - what's the alternative of require.context in vitejs

I'm looking for way to make the same logic of require.context of webpack in vitejs, I've found this plugin named vite-plugin-import-context, I tried it out but there's something that I didn't understand which is import dynamicImport from '../src/index' in the basic usage :
import { UserConfigExport } from 'vite';
import vue from '#vitejs/plugin-vue';
import dynamicImport from '../src/index';// <-- this is not described
export default (): UserConfigExport => {
return {
plugins: [vue(), dynamicImport(/*options*/)],
};
};
require should never be used in source code when using Vite. It's ESM only.
For Vite v2, import.meta.globEager can be used.
For Vite >v2, import.meta.globEager is deprecated. Use import.meta.glob('*', { eager: true }) instead.
import.meta.glob() is webpack alternative of require.context() . It has been added in vite since v2.3.4 .
Here is a link to the doc https://vitejs.dev/guide/features.html#glob-import
Yep, that example is directly taken from examples folder in the repo so it works only in that repo.
If you install the plugin via npm or yarn, the line should look like import dynamicImport from 'vite-plugin-import-context'
Here is how I import all the plugins/modules:
main.ts
import { createApp } from 'vue';
import App from './App.vue';
const app = createApp(App);
// styles here
import '#/assets/styles/tailwind.css';
import '#/assets/styles/main.scss';
// install all modules under `modules/`
Object.values(
import.meta.glob<{ install: (ctx: any) => void }>('/src/modules/*.ts', { eager: true })
).forEach((module) => module.install?.(app));
app.mount('#app');
and here is how I keep things ready in my modules folder to export:
modules/pinia.ts
import { createPinia } from 'pinia';
export const install = (app: any) => {
app.use(createPinia());
};
modules/router.ts
import type { RouteRecordRaw } from 'vue-router';
import { createRouter, createWebHashHistory } from 'vue-router';
import generatedRoutes from '~pages';
import { setupLayouts } from 'virtual:generated-layouts';
const routes: RouteRecordRaw[] = setupLayouts(generatedRoutes);
const router = createRouter({
history: createWebHashHistory(),
routes,
});
export const install = (app: any) => {
app.use(router);
};

vue show page after i18n translations loaded from backend

In my application translations can be edited by admin, so I need to preload them from my backend and initialize in the vue apps I have. So I found that vue i18n package has the ability to lazy load messages (link) . But on their example, they already initialize vue with default (en) messages, but in my case, I do not have default messages preloaded. So I want to load messages from the backend at a moment of creating a new instance of the VueI18n.
Here is the i18n.js file:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import {syncGetData} from "#/apiUtil";
import NProgress from 'nprogress'
import './sass/nprogress.scss'
Vue.use(VueI18n)
let loadedLanguages = []
export const i18n = new VueI18n({
locale: navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE,
fallbackLocale: navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE,
messages: loadLanguage(navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE)
})
function setI18nLanguage (lang) {
i18n.locale = lang
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguage(lang) {
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
NProgress.start();
return syncGetData('/api/translations/' + lang)
.then(function(data) {
setI18nLanguage(lang)
loadedLanguages.push(lang)
return data;
})
.catch(function (error) {
console.error(error)
}).finally(function () {
NProgress.done();
});
}
also here is the main.js (simplified):
import Vue from 'vue'
import App from './App.vue'
import {i18n} from './i18n'
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
The loading of the translations working, but Vue render the template before they loaded, and instead of actual translations I see translation keys. I tried to use v-cloak but it did not help.
So what I want to achieve is, while translations loading users should see only the loading bar (I'm using NProgress, you can see the usage in the i18n.js) and render the app only after translations loaded (maybe not render but initialize). What I'm getting instead: both loading bar and rendered app without actual translations
Thanks in advance!
Updates after Michal LevĂ˝ asnwer
Now, i18n.js looks like this:
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import { syncGetData} from "#/apiUtil";
import NProgress from 'nprogress'
import './sass/nprogress.scss'
Vue.use(VueI18n)
let loadedLanguages = [];
export function initI18n() {
const lang = navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE
return syncGetData('/api/translations/' + lang)
.then(function(data) {
console.log(data)
setI18nLanguage(lang)
loadedLanguages.push(lang)
return new VueI18n({
locale: lang,
fallbackLocale: lang,
messages: data
})
})
.catch(function (error) {
console.error(error)
});
}
function setI18nLanguage (lang) {
document.querySelector('html').setAttribute('lang', lang)
return lang
}
export function loadLanguage(lang) {
// If the language was already loaded
if (loadedLanguages.includes(lang)) {
return Promise.resolve(setI18nLanguage(lang))
}
NProgress.start();
return syncGetData('/api/translations/' + lang)
.then(function(data) {
setI18nLanguage(lang)
loadedLanguages.push(lang)
return data;
})
.catch(function (error) {
console.error(error)
}).finally(function () {
NProgress.done();
});
}
and main.js is:
import Vue from 'vue'
import App from './App.vue'
import {initI18n} from './i18n'
import NProgress from "nprogress";
import './sass/nprogress.scss'
NProgress.start();
initI18n().then(function(i18n) {
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
}).finally(function () {
NProgress.done();
});
I can confirm that now messages are loaded before the Vue initalized, b-z in my App.vue I have a console.log in mounted method:
mounted() {
console.log(this.$i18n.messages)
}
and the output of console log is Object { main_title: "Calculation", tab_you: "You", tab_friend: "Your Friend",...} , before it was empty object.
But still <h2 class="title-form">{{ $t('main_title') }}</h2> renders main_title instead of Calculation
Replace VueI18n innitialization with something like this:
export function initI18n() {
const lang = navigator.language.split('-')[0] || process.env.VUE_APP_I18N_LOCALE
return loadLanguage(lang).then(function(data) {
return new VueI18n({
locale: lang,
fallbackLocale: lang,
messages: data
})
})
}
main.js
import Vue from 'vue'
import App from './App.vue'
import { initI18n } from './i18n'
initI18n().then(function(i18n) {
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
})
Edit after Q update:
It seems the format of your localization is wrong. VueI18 is able to use multiple languages at once so all your translation keys should be contained in "language object"
Instead of:
{
main_title: "Calculation",
tab_you: "You",
tab_friend: "Your Friend",
...
}
your API should return
{
"en": {
main_title: "Calculation",
tab_you: "You",
tab_friend: "Your Friend",
...
}
}
...where "en" is language identifier
Check your Browser console, I believe VueI18n should warn about missing keys...

How can I fix '[vuex] unknown action type: lookupName' within my Vue component?

I am having an issue calling up an action from my Vue component using this.$store.dispatch('lookupName'). I have tried console logging the this.$store method, but the actions within it are empty.
Main.js
import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
import { findCertificate } from '#/api';
Vue.use(Vuex)
const state = {
// single source of data
nameLookup: {},
}
const actions = {
// asynchronous operations
lookupName(context, form){
return findCertificate(form)
}
}
const store = new Vuex.Store({
state,
actions,
mutations,
getters
})
export default store
Vue Component
import VueRecaptcha from 'vue-recaptcha';
import { mapGetters, mapActions} from 'vuex'
export default {
created() {
},
methods: {
...mapActions({
lookupName: 'lookupName'
}),
...mapActions({
add: 'lookupName'
}),
onCaptchaVerified: function(recaptchaToken){
this.$refs.recaptcha.reset();
console.log(this.$store.dispatch('lookupName', this.form))
},
}
}
Why am I still getting this error? I have looked up many other people's questions about similar issues, but those solutions did not work for me.

Vuejs - How to call the mounted function of main.js from another Js file

This is the code I have given in commonValidation.js.
commonValidation.js:
this.$validator.localize('en', {
messages: {
required: (field) => '* ' + field + ' is required'
},
attributes: {
email: 'Email'
}
})
I want to call the above file in main.js inside the mounted function
like below. But it's not working. If I given those validation(from commonValidation.js) inside the mounted()(in main.js) method it's working.
main.js:
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import App from './App'
import router from './router'
import VeeValidate from 'vee-validate';
import commonValidation from './commonValidation'
Vue.use(VeeValidate);
Vue.use(BootstrapVue);
new Vue({
el: '#app',
router,
template: '<App/>',
components: {
App
},
mounted()
{
commonValidation
}
})
Please help me to call the commonValidation.Js inside the mounted() in main.js . Thanks in advance.
This is my complete commonValidation.js
export default {
mounted() {
this.$validator.localize('en', {
messages: {
required: (field) => '* ' + field + ' is required'
},
attributes: {
email: 'Email'
}
})
}
}
You are exporting an object in commonValidation.js file.
An object cannot be invoked like a function.
I think your intent is to use a mixin. A mixin is nothing but an object that contains reusable component options as its properties.
So just register the mixin on the root component in your main.js file:
//main.js
import Vue from 'vue'
import BootstrapVue from 'bootstrap-vue'
import App from './App'
import router from './router'
import VeeValidate from 'vee-validate';
import commonValidation from './commonValidation'
Vue.use(VeeValidate);
Vue.use(BootstrapVue);
new Vue({
el: '#app',
router,
template: '<App/>',
components: {
App
},
mixins : [commonValidation]
}