Cannot read property '$nuxt' of undefined - javascript

I am working on nuxt.js project and getting an error Cannot read property '$nuxt' of undefined when trying to access an event from plugin.
In ~/plugins/myPlugin.js
import Vue from 'vue';
this.$nuxt.$on('emit-height', (payload) => {
Vue.prototype.$bannerHeight = payload;
});
Importing in ~/plugins/nuxt.config.js
plugins: [
'~/plugins/get-main-banner-height.js',
]
this.$nuxt.$on works if I use it in any components but doesn't work in plugin as mentioned above.
In my component I am emitting the height.
methods: {
getMainBannerHeight() {
this.$nextTick(() => {
this.$nuxt.$emit('emit-main-banner-height', this.bannerHeight);
});
},
}
So, my question is "How to listen/capture event in plugins"?

You can reference app in context of nuxt plugin. Docs https://nuxtjs.org/api/context/
import Vue from 'vue';
export default ({ app }) => {
app.$on('emit-height', (payload) => {
Vue.prototype.$bannerHeight = payload;
});
}

Related

TS/Vue 3: Can't access mocked global property in component

I am using the v3-tour plugin for a Vue 3 frontend, and I access the global $tours property injected by the plugin in my component like so
<script setup lang="ts">
import { ComponentPublicInstance, computed, getCurrentInstance, onMounted } from 'vue';
import { useI18n } from 'vue-i18n';
const app = getCurrentInstance();
const proxy = app?.appContext.config.globalProperties;
...
const startTour = () => {
proxy?.$tours['homeTour'].start();
};
...
This works but a problem occurs in my unit tests when I try to mock $tours like so:
const wrapper = shallowMount(Tour, {
global: {
stubs: ['v-tour', 'v-step'],
mocks: {
$tours: {
homeTour: {
start: jest.fn(),
currentStep: 0,
nextStep: jest.fn(),
},
},
},
plugins: [i18n],
},
});
When I try to test the help tour
it('startTour should start tour', () => {
wrapper.find("#start-tour").trigger("click");
expect(wrapper.vm.$tours['homeTour'].start).toHaveBeenCalled();
});
it fails because $tours in undefined in my component:
Cannot read properties of undefined (reading 'homeTour')
How can should I mount or access $tours so that I can access it in my unit tests ?

Access Vuex Store in Blade file | Laravel - Vue js

How can I access a method or variable defined in the Vuex 4 store in my Blade file?
I am using a compiled app.js from vite. Obviously in the components of Vue js I can access the store, I wonder if it is also possible to do it in a blade file.
Vue js #app instance must be one of course.
If at the end of my blade file I write this
<script>
import {useStore} from "vuex";
import {onMounted, watch, ref, defineComponent} from 'vue'
export default {
setup() {
const click = () => {
store.commit('mutazione');
};
onMounted(() => {
alert('test');
})
const store = useStore();
return {
store,
click
}
},
}
</script>
the console gives me this type of error
Unexpected token '{'. import call expects exactly one argument.
First Leave laravel out of it.
It is purely vuex5.0 / Pinia https://pinia.vuejs.org job to do.
Best practice is Create the store of every type of data you need like
https://github.com/puneetxp/the check test directly in it totally in JavaScript you can see like create Store assume it is for Product data.
import { defineStore, acceptHMRUpdate } from "/js/vue/pinia.js";
export const useProductStore = defineStore({
id: "Product",
state: () => ({
rawItems: [],
}),
getters: {
items: (state) => state.rawItems
},
actions: {
addItem(product) {
this.rawItems.push(product)
},
removeItem(id) {
this.rawItems = this.rawItems.filter(i => i.id != id);
},
editItem(product) {
this.rawItems = this.rawItems.filter(i => i.id != product.id);
this.rawItems.push(product);
},
upsertItem(products) {
products.forEach(product => {
this.rawItems = this.rawItems.filter(i => i.id != product.id);
this.rawItems.push(product);
});
}
},
})
if (import.meta.hot) {
import.meta.hot.accept(acceptHMRUpdate(useActive_roleStore, import.meta.hot))
}
then we can use in your component
import { useProductStore } from "/js/vue/Store/Model/Product.js";
export default {
template: ``,
data() {
return {
useProductStore,
}
}
}
for use use it on useProductStore().items as my design you can make your own if you want.

Why Vue global EventBus do not work in my project

I am using vue js in laravel, I need to use the global event bus. I created event-bus.js, and I import it to where I need to use it; when I click, events are generated, but there are no reactions from the listener. I tried everything, but it doesn’t work. Please help me, I have been working for 3 days
I tried to create an eventBus in app.js, but it didn't help either
My EditDiscountComponent
import { EventBus } from "../../../event-bus";
editDiscount () {
const data = {
discount_id: this.discountData.discount_id,
status: this.discountData.status,
type: this.discountData.type,
percentage:this.discountData.percentage,
amount:this.discountData.amount,
};
EventBus.$emit('update-discount', data);
this.languages.forEach(lang => {
if (lang.code && this.discountData[lang.code] !== undefined) {
data[lang.code] = this.discountData[lang.code];
}
});
this.$store.dispatch(actions.EDIT_DISCOUNT, data)
.then(res => {
if (res && res.data.status) {
// window.location.href = '/admin/discounts';
} else {
this.$store.commit(mutations.SET_SNACKBAR_SHOW, true);
this.$store.commit(mutations.SET_SNACKBAR_TEXT, res.data.message);
}
}).catch(console.error);
},
},
My ProductCOmponent
import { EventBus } from "../../../event-bus";
mounted() {
EventBus.$on('update-discount', ($data) => {
console.log($data);
});
this.getCategories();
this.getProducts();
},
I tried the callback of the function using a different method, but no results, and I tried this listener in the mount (), and this did not help
You can create your eventBus.js file inside src folder of your project which will look something like this
import Vue from 'vue'
export default new Vue()
And try to import it as shown below:
import EventBus from '#/eventBus'
Or this might help you https://alligator.io/vuejs/global-event-bus/

Using 1 Axios call for multiple components

I am running a simple Axios call like so:
.get('https://myAPI.com/')
.then(response => {
this.info = response.data
})
And then display the data through a v-for array loop on my components. The problem is that I am running this mounted Axios call on each component I use it for. For example, I have a component for desktop screens that uses this axios call to display data in sidebar, while my mobile screen component uses the exact same axios call too display in a header.
The problem is that I am running multiple calls to the same API since each component is using the mounted axiox function.
Is there a way to run this call once and then utilize the v-for loop on each component?
Use Vuex for such task.
I'll make a very simple example.
Install vuex and axios in your project
later create a file in your project call, store.js.
store.js
import Vue from "vue";
import Vuex from "vuex";
import axios from "axios";
const store = new Vuex.Store({
state: {
info : []
},
mutations: {
updateInfo (state, info) {
state.info = info
}
},
actions: {
fetchData({commit}) {
axios.get('https://myAPI.com/')
.then(response => {
commit('updateInfo', response.data )
})
}
}
})
in your main.js import store.js file
import store from "./store";
new Vue({
...
store,
...
});
in your App.vue dispatch 'updateInfo' action.
App.vue
...
created() {
this.$store.dispatch("fetchData");
}
...
And in the component you want to use the info data component, set:
...
computed: {
info() {
return this.$store.state.info
}
},
...
and use info to render the elements with the v-for directive.
This info refers the array of elements you bring
OK, I've found a way to handle this without Vuex. My example: I have two components TrainingCourseComponent and CertificateComponent.
In TrainingCourseComponent:
data() {
return {
trainings : {},
},
methods:{
loadTrainingCenters(){
axios.get("/trainingCourse")
.then(({data}) => {
this.trainings = data;
Event.$emit('globalVariables', data);
});
}
}
created(){
this.loadTrainingCenters();
}
and you can do this in any other component but in this case CertificateComponent(you can define it in mounted() or created() method it doesn't matter:
data() {
return {
training_courses:{}
}
}
mounted(){
Event.$on('globalVariables', (trainings) => {
this.training_courses = trainings;
});
}
p.s. I guess you know but just in case Event is a global Vue instance defined in app.js that I use for different kind of stuff :)
app.js
/**
* some custom event
*/
window.Event = new Vue();

Property or method not defined on the instance but referenced during render I Vuex

I'm getting the following error.
[Vue warn]: Property or method "updateData" is not defined on the instance but referenced during render. Make sure to declare reactive data properties in the data option.
As far I can tell by the code, the method is there, so I'm stuck on something that I miss due to my ignorance of Vuex. I've googled the matter and got quite a few answers but none of them made me any wiser what to do. It seems to be something with scope, I'm sensing.
I also get the error below but I suspect that it's the same root cause for both so solving the one will resolve the other.
[Vue warn]: Invalid handler for event "click": got undefined
(found in component at ...)
The markup is as follow. I've checked that the path goes to the right location. At the moment I'm not sure at all how to even start to troubleshoot it. Any hints would be appreciated.
<template>
<div id="nav-bar">
<ul>
<li #click="updateData">Update</li>
<li #click="resetData">Reset</li>
</ul>
</div>
</template>
<script>
import { updateData, resetData } from "../vuex_app/actions";
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData }
}
}
</script>
Edit
After input I improved the export to include methods property like so. (Still the same error remaining, though.)
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData },
methods:{
updateData: () => this.$store.dispatch("updateData"),
resetData: () => this.$store.dispatch("resetData")
}
}
}
Do I have to do something extra in the store? It looks like this.
import Vue from "vue";
import Vuex from "vuex";
Vue.use(Vuex);
const state = { dataRows: [], activeDataRow: {} };
const mutations = {
UPDATE_DATA(state, data) { state.dataRows = data; state.activeDataRow = {}; },
RESET_DATA(state) { state.dataRows = []; state.activeDataRow = {}; }
};
export default new Vuex.Store({ state, mutations });
You have to add the imported functions in the methods of Vue component, like following. You can take help of mapActions as explained in the documentation. This is needed to map this.updateDate to this.$store.dispatch('updateDate').
<script>
import { updateData, resetData } from "../vuex_app/actions";
import { mapActions } from 'vuex'
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData }
},
methods: {
...mapActions(['updateData', 'resetData'])
}
}
</script>
Edited
In case you dont want to use mapActions, you can use this.$store.dispatch as you are using in your example, however you need to have methods at vue compoenent level (documentation) and not insise vuex, as following:
export default {
vuex: {
getters: { activeDataRow: state => state.activeDataRow },
actions: { updateData, resetData }
},
methods:{
updateData: () => this.$store.dispatch("updateData"),
resetData: () => this.$store.dispatch("resetData")
}
}

Categories