Using momentJS globally in VueJS 3 - javascript

In VueJS 2, I could use momentJS globally like this:
in main.js:
import moment from 'moment';
moment.locale('fr');
Object.defineProperty(Vue.prototype, '$moment', { value: moment });
in any component:
{{ item.date_creation?$moment(item.date_creation).format('l'):'No date'}}
In VueJS 3 I tried to do the following in main.js:
import { createApp } from 'vue';
import moment from 'moment';
import App from './App.vue';
import './registerServiceWorker';
import router from './router';
import store from './store';
moment.locale('fr');
createApp(App).use(moment).use(store).use(router)
.mount('#app');
But actually I can't use "moment" in any of my component, I have to import "moment" in each of them.
Isn't there a better solution? I read a lot of documentation and tutorials, I didn't see anything good...
Thanks in advance.
Edit: here is how I use $moment in my component:
<template>
<div class="xx" :key="'title-'+myReactiveVariable.title">
<h1>{{ myReactiveVariable.title }}</h1>
<div class="yy">
{{ myReactiveVariable.content }}
</div>
<footer>
Sent on
{{ myReactiveVariable.date }}
by {{ myReactiveVariable.author }}
<who-is-it :idSent="id" :key="'ID-'+id">
</who-is-it>
</footer>
</div>
</template>
<script>
// # is an alias to /src
import WhoIsIt from '#/components/WhoIsIt.vue';
import {
defineComponent, reactive, onMounted, watch, ref,
} from 'vue';
import axios from 'axios';
import { useRoute } from 'vue-router';
export default defineComponent({
name: 'XXX',
components: {
WhoIsIt,
},
setup() {
const route = useRoute();
const myReactiveVariable = reactive({
title: '',
content: '',
date: '',
});
const id = ref('');
async function fetchArticle(id) {
console.log(`A: ${process.env.VUE_APP_API_URL} - ${id}`);
const res = await axios.get(`${process.env.VUE_APP_API_URL}/whoarewe/${id}`);
id.value = res.data.author;
myReactiveVariable.titre = res.data.title;
myReactiveVariable.content = res.data.content;
myReactiveVariable.date = this.$moment(res.data.date).format('l');
}
watch(() => route.params.id, () => {
fetchArticle(route.params.id);
});
onMounted(async () => {
fetchArticle(route.params.id);
});
return {
myReactiveVariable,
id,
};
},
});
</script>

Add the moment to the global config properties app.config.globalProperties.$moment=moment:
import { createApp } from 'vue';
import moment from 'moment';
import App from './App.vue';
import './registerServiceWorker';
import router from './router';
import store from './store';
moment.locale('fr');
let app=createApp(App);
app.config.globalProperties.$moment=moment;
app.use(store).use(router)
.mount('#app');
then use this.$moment in any child component with option api, with composition you should use getCurrentInstance :
import { getCurrentInstance } from 'vue'
setup(){
const internalInstance = getCurrentInstance()
...
myReactiveVariable.date = internalInstance.appContext.config.globalProperties.$moment(res.data.date).format('l');
}
You couldn't do app.use(moment) since moment is not a Vue plugin.

Related

Get global settings with Vue 3, Laravel, Vuex, axios

I am using Laravel 8, Vue 3 with vuex, axios.
I created an API at domain.com/api/settings to store global settings as: app name, store name, store description, ... These parameters are used in many components: MainHeader, MainFooter, child component
API domain.com/api/settings
{"data":[{"name":"app_name","val":"Laravel App"},{"name":"store_name","val":"Golf"},{"name":"store_description","val":"This is meta description"}]}
app.js
import { createApp, onMounted } from 'vue'
import router from './router'
import store from './store'
import MainHeader from './components/layouts/MainHeader.vue'
import MainFooter from './components/layouts/MainFooter.vue'
const app = createApp({})
app.component('main-header', MainHeader)
.component('main-footer', MainFooter)
app.use(router).use(store).mount('#app')
I can use API in seperated component like this:
resources/js/composables/settings.js
import { ref } from 'vue'
import axios from 'axios'
export default function useSettings() {
const settings = ref({})
const getSettings = async () => {
let response = await axios.get('/api/settings/')
settings.value = response.data.data.reduce((obj, item) => (obj[item.name] = item.val, obj) ,{})
}
return {
settings,
getSettings,
}
}
resources/js/components/layouts/MainFooter.vue
<template>
{{ settings.store_name }}
</template>
<script setup>
import { onMounted } from 'vue'
import useSettings from '../../composables/settings.js'
const { settings, getSettings } = useSettings()
onMounted(getSettings)
</script>
I wrote same code for MainHeader.vue, Contact.vue so the contact page will call API 3 times.
I tried to create js/store/index.js but it is not working
import { createStore } from 'vuex'
import { onMounted, ref } from 'vue'
import axios from 'axios'
export default createStore({
state: {
settings: () => {
const settings = ref({})
let response = axios.get('/api/settings/')
settings.value = response.data.data.reduce((obj, item) => (obj[item.name] = item.val, obj) ,{})
return {
settings
}
}
}
})

Can't use Vuex to display data

I am a new .net+vuejs learner and i'm using
this project template for vuejs
I'm trying to use vuex in my project to display countries data from database but it doesn't work
main.js
import 'bootstrap/dist/css/bootstrap.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
index.js
import { createApp } from "vue";
import App from "./App.vue";
import Vuex from "vuex";
import countries from "./module/countries/countries";
createApp(App).use(Vuex);
export default new Vuex.Store({
modules: {
countries: countries
}
});
store file
import axios from "axios";
const state = {
countries:[]
};
const countries = {
async getCountries({ commit }, payload) {
const result = await axios.get("Pho/GetCountries");
return result.data;
}
};
export default {
namespaced: true,
state,
mutations,
countries,
getters
}
my component
<script>
import axios from 'axios'
import swal from 'sweetalert';
export default {
name: "Home",
data() {
return {
countries: [],
}
},
methods: {
getCountries: async function () {
this.countries = await this.$store.dispatch("countries/getCountries");
}
},
mounted() {
this.getCountries();
}
}
</script>
ERROR: Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'dispatch')
i tried to import store to my main.js like this
import 'bootstrap/dist/css/bootstrap.css'
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import store from './index.js'
createApp(App).use(router, store).mount('#app')
but the app doesn't ever lauch
It is not good practice to call APIs from storage files. Use vuex storage for only storage, use mixins(recommended) or component methods for sending API. I will show a simple approach to managing vuex storage!
Storage File: index.js
import Vue from 'vue'
Vuex from 'vuex'
import router from '../router/index'
Vue.use(Vuex)
export const store = new Vuex.Store({
state: {
countries:[]
},
getters: {},
mutations: {
countriesChanger(state, payload) {
state.countries = payload;
},
},
actions: {},
});
Component File: MyComponent.vue
<template>
<li v-for="country in countries">
{{country}}
</li>
</template>
<script>
export default {
name: "Home",
data() {
return {
countries: [],
}
},
mounted() {
this.getCountries(); // assume this function calls to your API
},
watch: {
"$store.state.countries": function () {
// Listens when countries in storage updated
this.countries = this.$store.state.countries;
}
}
</script>
To change vuex storage item (to update countries) , use mutations after getting data from your API:
this.$store.commit("countriesChanger", countriesFromAPI);
That's it!

Component Not Found (Vuetify & Vuejs)

Component not found error. In vuetify 1.5.5 and vue 2.6.12 with vue-template-compiler 2.6.12. I have tried some guys on Discord but they cant seem to figure out what might be the problem here. Sometimes it issues our=t the component not found error, sometimes it doesnt run at all even if both libraries are the same version.
App.Vue
<template> <v-app>
<v-content>
<router-view></router-view>
</v-content></v-app>
</template><script>
export default {
name: "App",
components: {},
mounted() {},
data: () => ({}),
};
</script>
<style>
</style>
So the main.js contains mostly imports from javascript and initialization of libraries and modules main.js
import Vue from "vue";
import App from "./App.vue";
import "./registerServiceWorker";
import router from "./router";
import store from "./store/index";
import 'material-design-icons-iconfont/dist/material-design-icons.css';
import "./plugins/vuetify";
import 'animate.css'
import './assets/custom.css'
import './assets/table.css'
import './assets/google-like-text.css'
import VeeValidate from "vee-validate";
import VueSession from "vue-session";
import VueGoogleCharts from 'vue-google-charts';
import 'vue-orgchart/dist/style.min.css'
import Snotify from 'vue-snotify'
import 'vue-snotify/styles/material.css';
Vue.use(VeeValidate, { inject: false });
var options = {
persist: true
};
Vue.use(VueSession, options);
Vue.use(VueGoogleCharts);
Vue.use(Snotify)
Vue.config.productionTip = false;
new Vue({
router,
store,
render: h => h(App)
}).$mount("#app");
And vuetify.js
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
import 'vuetify/src/stylus/app.styl'
import 'vuetify/dist/vuetify.min.css'
import colors from "vuetify/es5/util/colors"
Vue.use(Vuetify, {
iconfont: 'md',
theme: {
primary: "#2E7D32",
secondary: colors.grey.darken1,
accent: colors.shades.black,
error: colors.red.accent3
}
})

[Vue warn]: Computed property "axios" is already defined in Data. at <App>

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

Vue composition API use VueAxios?

I am in main.js importing vue-axios
main.js
import { createApp } from 'vue';
import axios from 'axios';
import VueAxios from 'vue-axios';
import App from './App.vue';
const app = createApp(App);
app.use(VueAxios, axios);
app.mount('#app');
and App.vue
export default {
name: 'App',
setup() {
axios.get('xxxx').then((res) => {console.log(res)}); // is not working
}
};
but it seems Vue composition API setup can't get axios? So it must be used in App.vue import axios from 'axios'?
import axios from 'axios';
export default {
name: 'App',
setup() {
axios.get('xxxx').then((res) => {console.log(res)});
}
};
My environment is vite
generally I'd do something like this ( except I modularize all the api calling into a "service" rather than inlining it into the view code.)
import axios from 'axios';
import {onMounted} from 'vue'
export default {
name: 'App',
setup() {
onMounted(async () => {
let res = await axios.get('xxxx')
console.log(res)
});
}
};
This wouldn't work in any version of Vue without an import:
axios.get(...)
Even in Vue 2, you have to use one of these if you use vue-axios:
this.axios.get(...)
this.$http.get(...)
Vue.axios.get(...)
The composition API has no this access in the component, so vue-axios doesn't help much.

Categories