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

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
}
}
}
})

Related

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!

Couldn't access state in vuex 4

I'm using vuex v4 with composition api. The problem is I couldn't access store states in components but I can see them in vue-devtools. When I wanna access store state it gave me undefined.
store/index.ts:
import { InjectionKey } from "vue";
import {
createStore,
createLogger,
Store,
useStore as vuexUseStore,
} from "vuex";
import { ConfigStateType } from "./config/state";
import config from "./config";
export enum ModuleTypes {
CONFIG = "CONFIG",
}
export interface StateInterface {
config: ConfigStateType;
}
export const key: InjectionKey<Store<StateInterface>> = Symbol();
export const store = createStore<StateInterface>({
modules: {
[ModuleTypes.CONFIG]: config,
},
strict: debug,
});
export function useStore() {
return vuexUseStore(key);
}
vue js main.ts:
import { store, key } from "./store";
createApp(App).use(store, key).use(router).mount("#app");
In my component:
import { useStore } from "#/store";
setup() {
const store = useStore();
const recaptchaSiteKey = computed(
() => store.state.config.googlerecaptcha_site_key
);
return {
recaptchaSiteKey
}
}
always recaptchaSiteKey returns undefined, but in vue-devtools states are filled.
vuex devtool:

how can I get data property from ref object in vue3

First I create a ref object named teamData, then fetch service api and assign to teamData.
But what should I do get data property from teamData. for example I want to get title property from teamData, but result is unxexpected
here is the code
// useTeamData.js
import { fetchTeamGoal } from '#/api/team'
import { onMounted, watch, ref} from 'vue'
export default function getTeamData(date) {
const teamData = ref({})
const getTeamGoals = async () => {
await fetchTeamGoal(date.value + '-01').then(res => {
teamData.value = res.data
})
}
onMounted(getTeamGoals)
watch(date, getTeamGoals)
return {
teamData,
getTeamGoals
}
}
// test.vue
import dayjs from 'dayjs'
import useTeamData from '#/components/composables/useTeamData'
import {defineComponent, ref, toRefs, computed, provide, toRaw, reactive} from 'vue'
import { useStore } from 'vuex'
export default defineComponent({
name: 'Test',
setup: () => {
const store = useStore()
const userInfo = computed(() => store.state.user.userInfo)
const date = ref(dayjs().format('YYYY-MM'))
const { teamData } = useTeamData(date)
console.log(teamData, 'teamData')
console.log(teamData.title)
console.log(teamData.value.title)
provide('role', userInfo.value.role)
return {
date,
userInfo,
teamData,
}
},
})
here is the browser output image
It looks like it's a lifecycle issue, because the code inside the setup hook is ran before the code inside onMounted hook, to solve this try out to use a watch :
import {defineComponent,watch, ref, toRefs, computed, provide, toRaw, reactive} from 'vue'
...
const { teamData } = useTeamData(date)
watch(()=>teamData,(newVal,oldVal)=>{
console.log(newVal.value.title)
},
{
deep:true,
immediate:true
})

Setting data in state not working in Vue 3 with Vuex 4

I'm learning Vue 3 with Vuex 4 and I'm stucked with something that I'm pretty sure it's simple but I can't see.
In few words, i'm trying to set some data in state to have it available to use it in my components but it isn't working.
Let me show you the code:
/// store.js
import { createStore } from 'vuex';
import axios from 'axios';
const store = createStore({
state: {
user: {},
products: []
},
mutations: {
SET_USER: (state, user) => {
state.user = user;
},
SET_PRODUCTS: (state, products) => {
state.products = products;
},
},
actions: {
GET_USER: async function ({ commit }) {
const user = await axios.get('https://coding-challenge-api.aerolab.co/user/me')
commit('SET_USER', user)
},
GET_PRODUCTS: async function ({ commit }) {
const products = await axios.get('https://coding-challenge-api.aerolab.co/products')
commit('SET_PRODUCTS', products)
},
}
})
export default store;
/// MyComponent.vue
<template>
<div class='bg-aerolab-main'>
{{ user }} {{ productsTest }}
</div>
</template>
import { computed } from "vue";
import { useStore } from 'vuex';
export default {
setup() {
const store = useStore();
const user = computed(() => store.state.user);
const productsTest = computed(() => store.state.products);
console.log(user);
console.log(productsTest);
return {
user,
productsTest
};
}
}
/// main.js
import { createApp } from 'vue'
import App from './App.vue'
import './index.css'
import axios from 'axios'
import VueAxios from 'vue-axios';
import store from './store';
const app = createApp(App)
app.use(VueAxios, axios)
app.use(store)
app.mount('#app')
Of course the {{ users }} and {{ productsTest }} bindings are not displaying any data aswell as both console.logs.
PD: I've tried to fetch the data directly in my component and it's working so it's not something related to the data fetched from the API.
You've to dispatch that actions inside mounted hook :
import { computed , onMounted} from "vue";
import { useStore } from 'vuex';
export default{
setup() {
const store = useStore();
const user = computed(() => store.state.user);
const productsTest = computed(() => store.state.products);
onMounted(()=>{
store.dispatch('GET_USER');
store.dispatch('GET_PRODUCTS');
})
return {
user,
productsTest
};
}
}
Fetch Data from main.js
I'm sure the selected answer would work properly if you are fetching data from your component(s). However, if you are looking to Fetch Data from main.js as OP was asking or app.js if you are using Vue3 with Laravel, you should use mounted() in your createApp() function.
const app = createApp({
mounted(){
store.dispatch('YOUR_ACTION') // GET_USER or GET_PRODUCTS in OP's case
}
});
Remember to set up your Vuex store before calling this so the store is defined.
In your components
You can access the computed state with composition API, as shown below:
import { computed } from "vue";
import { useStore } from "vuex";
setup() {
const store = useStore();
return {
products: computed(() => store.state.YOUR_STATE),
//replace YOUR_STATE with your own. In OP's case, it's user or products
};
}

[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

Categories