I have setup mitt and trying to dispatch event to another component but I am having hard time because in the setup() method it doesn't have this for accessing app instance.
Here is what I tried:
import App from './App.vue'
const el = document.getElementById('app')
import mitt from 'mitt';
const emitter = mitt();
const app = createApp(App)
app.config.globalProperties.emitter = emitter;
app.mount(el);
And in the component, I want to dispatch an event
export default {
setup() {
function toggleSidebar() {
this.emitter.emit('toggle-sidebar');
console.log(this); // binds to setup(), not the vue instance.
}
}
}
As this doesn't exist, I can't access the .emitter. What am I missing? How to use officially suggested mitt in Vue 3 composition api?
By the way if I use the v2 syntax, I can access this.emitter. But I am curious about Composition API way
export default {
mounted() {
console.log(this.emitter); // works
}
}
To use an event bus in Vue 3 Composition API, use Vue 3's new provide api in main.js, and then inject in any component:
1. Install mitt:
npm install mitt
2. Provide:
main.js
import { createApp } from 'vue';
import App from './App.vue';
import mitt from 'mitt'; // Import mitt
const emitter = mitt(); // Initialize mitt
const app = createApp(App);
app.provide('emitter', emitter); // ✅ Provide as `emitter`
app.mount('#app');
3. Inject
3a. Any Component - Emit an event
import { inject } from 'vue'
export default {
setup() {
const emitter = inject('emitter'); // Inject `emitter`
const mymethod = () => {
emitter.emit('myevent', 100);
};
return {
mymethod
}
}
}
Call mymethod from a button click or something.
3b. Any Component - Listen for the event
import { inject } from 'vue'
export default {
setup() {
const emitter = inject('emitter'); // Inject `emitter`
emitter.on('myevent', (value) => { // *Listen* for event
console.log('myevent received!', `value: ${value}`);
});
},
}
Console
myevent received! value: 100
You may be able to use getCurrentInstance to get the global property
component:
import { getCurrentInstance } from 'vue';
export default {
setup() {
// get current instance
const internalInstance = getCurrentInstance();
// get the emitter from the instance
const emitter = internalInstance.appContext.config.globalProperties.emitter;
}
}
So far I have used this code to make the "emitter" available.
//main.ts
import mitt from 'mitt'
const emitter = mitt()
export default emitter
And then inside the components I use
import emitter from '#/main';
This worked so far in Vue2 and Vue3 - at least with the options API.
I have to admit though that I currently run into some trouble with the new vite server and the hot module reload (hmr).
Is this style suboptimal in any way?
Related
Can someone help me out configuring a global function that I can call in all my Vue files?
When there is this in my Vue file:
#click="ModalShow.show('my-create')"
In the app.js I defined this constant:
const Modals = {
show(screen) {
alert(screen);
// other logic that i implement that should be triggered
},
};
But I keep getting:
TypeError: undefined is not an object (evaluating '_ctx.Modals.show')
What am I missing? It's a Vue project with the composition API
You can use provide/inject, first provide your function to your child components from the app (or parent component)
const Modal = {...}
const app = createApp({})
app.provide('Modal', Modal)
Then inject it into your component
import { inject } from 'vue'
export default {
setup() {
const Modal = inject('Modal')
return { Modal }
}
}
Or via script setup:
<script setup>
import { inject } from "vue";
const Modal = inject("Modal");
</script>
I'm trying to understand the purpose of composables. I have a simple composable like this and was trying to watch state from a Pinia store where the watch does not trigger:
import { ref, watch, computed } from "vue";
import { storeToRefs } from "pinia";
import useFlightsStore from "src/pinia/flights.js";
import usePassengersStore from "src/pinia/passengers.js";
export function useFlight() {
const route = useRoute();
const modalStore = useModalStore();
const flightsStore = useFlightsStore();
const { selection } = storeToRefs(flightsStore);
const passengersStore = usePassengersStore();
const { passengers, adults, children, infants } =
storeToRefs(passengersStore);
watch([adults, children, infants], (val) => console.log('value changes', val))
Where as the same thing in a Vue component works as expected.
So we cannot watch values inside composables?
I think you can watch values inside composables.
But, to watch a pinia state it has to be inside an arrow function:
watch(() => somePiniaState, (n) => console.log(n, " value changed"));
It's like watching a reactive object.
I believe this should be documented better. In Pinia documentation we can read how to watch the whole store or how to subscribe to a store but not how to watch a single state property inside a component or composable.
Also, the docs are somewhat shy in explaining that you can watch a property inside a store using setup() way of describing a store.
More on this here:
https://github.com/vuejs/pinia/discussions/794#discussioncomment-1643242
This error also silently fails (or does not execute), which is not helpful...
I needed to watch a specific state attribute in one of my components but I didn't find my use case on the official documentation.
I used a mix between a watch and storeToRefs to do it.
import { usePlaylistsStore } from '#/stores/playlists'
import { storeToRefs } from 'pinia'
export default {
name: 'PlaylistDetail',
setup() {
const playlistsStore = usePlaylistsStore()
const { selectedGenres } = storeToRefs(playlistsStore)
return { selectedGenres }
},
watch: {
selectedGenres(newValue, oldValue) {
// do something
}
}
}
So I am trying to import the ipcRenderer into a react component to communicate with the electron side. The issue is I cannot import electron. I tried
import { ipcRenderer } from 'electron/renderer'
returns module electron/renderer not found
import { ipcRenderer } from 'electron'
returns fs.existsSync is not a function
const renderer = require('electron');
returns fs.existsSync is not a function
const renderer = require('electron').ipcRenderer;
returns fs.existsSync is not a function
const renderer = window.require('electron');
returns window.require is not a function
I do not know what to do anymore, I have tried everything
I got it! using electron-react-bolierplate they prepared a custom preload.js script that exposes three functions to the rendered components: myPing: (Just a demo, send a ping message to the console) and exposes the on and once ipcRenderer methods
import { Component } from 'react';
...
...
class Hello extends Component {
componentDidMount() {
console.log('Mounted!', 'Window', window, 'electronApi', window.electron, 'ipcRenderer', window.electron.ipcRenderer);
window.electron.ipcRenderer.on('ipc-example', (arg) => {
// eslint-disable-next-line no-console
console.log(arg);
});
window.electron.ipcRenderer.myPing();
}
render() {
return (
<div>
...
...
I used to have same problem. You should not import electron directly inside your renderer/react component. Instead in your preload.ts file you are given some basic configuration by electron-react-bolierplate to use.
So inside your react component you should use window.electron.ipcRenderer.on('channel-name', args) sample below
const myEventHandler = () => {
window.electron.ipcRenderer.on('channel-name', (event, data) => {
console.log('data, event', data, event);
});
};
window.electron, here electron is the name given in preload.ts file. contextBridge.exposeInMainWorld('electron', {...})
Like the title, I want to add Axios into Vue prototype. So when I want to use it, I can use it like this.$axios instead of importing it every time.
CODE:
//plugins/axios.ts
import axios from 'axios'
import router from '../router/index'
const errorHandle = (): void => {};
const instance = axios.create({
// baseURL: process.env.NODE_ENV == 'development' ? '' : ''
baseURL: 'http://localhost:3000',
timeout: 1000 * 12
});
instance.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=UTF-8'
export default instance
import { createApp } from 'vue'
import App from './App.vue'
import installElementPlus from './plugins/element'
import instance from './plugins/axios'
import router from './router'
const app = createApp(App).use(router)
installElementPlus(app)
// app.config.globalProperties.http = instance
app.config.globalProperties.$axios= instance
app.mount('#app')
However, there is a problem when I try to use it like this: this.$axios.
TS2339: Property '$axios' does not exist on type 'ComponentPublicInstance<{}, {}, {}, {}, {}, EmitsOptions, {}, {}, false, ComponentOptionsBase<{}, {}, {}, {}, {}, ComponentOptionsMixin, ComponentOptionsMixin, EmitsOptions, string, {}>>'.
How could I fix this problem?
You could use provide/inject or define a global config property, which replaces Vue.prototype in Vue 3:
1. provide/inject (for Composition or Options API)
Provide
import axios from 'axios';
const app = Vue.createApp(App);
app.provide('$axios', axios); // Providing to all components during app creation
Inject
Composition API:
const { inject } = Vue;
...
setup() {
const $axios = inject('$axios'); // injecting in a component that wants it
// $axios.get(...)
}
Options API:
inject: ['$axios'], // injecting in a component that wants it
2. Global config (for Options API)
import axios from 'axios';
const app = Vue.createApp(App);
app.config.globalProperties.$axios = axios;
Options API:
this.$axios
Note: This is only for the Options API. Evan You (Vue creator) says: "config.globalProperties are meant as an escape hatch for replicating the behavior of Vue.prototype. In setup functions, simply import what you need or explicitly use provide/inject to expose properties to app."
There is a way to use globalProperties inside a setup function. Although this might be considered an anti-patter. It would be better to use provide/inject if possible. But if there is a library that uses globalProperties and you really need to access it from setup this is how you could do it.
<script setup>
import { getCurrentInstance } from 'vue'
const app = getCurrentInstance()
const progressBar = app.appContext.config.globalProperties.$Progress
progressBar.start()
</script>
I am a beginner in Vue.js and so this question might be duplicate or naive. I want to call functions defined in a custom javascript file within a Vue component. I did something like this.
custom.js
class API{
function testCall(){
alert("test ok");
}
}
export {API}
App.vue
<template>
<div id="app">
<img src="./assets/logo.png">
<HelloWorld msg="Welcome to Your Vue.js App"/>
<testcomponent :on-click="getData">
</testcomponent>
</div>
</template>
<script>
import HelloWorld from './components/HelloWorld.vue';
import TestComponent from './components/TestComponent.vue';
import API from './js/custom.js';
export default {
name: 'app',
components: {
HelloWorld,
TestComponent,
API
},
methods: {
getData(){
const apiObj = new API();
apiObj.testCall();
}
}
}
</script>
When I build using npm run build, I get below error.
Any help with this please?
1: To define methods in a class you do not need function keyword.
class API{
testCall(){
alert("test ok");
}
}
2: Since you are doing a named export using export {API}, your import statement should be
import {API} from './js/custom.js';
3:components options is for registering vue components locally. Since API is not a vue component remove it from the components option.
API is not a Vue component - you should not include it inside the components branch. Also, if this is just a bunch of utility functions you can either export them one by one or as a containing object
// util.js - individual functions
export function testCall (call) {};
export function testUser (user) {};
// Vue app
import { testCall, testUser } from 'util.js';
// util.js - object group
function testCall (call)
{
}
function testUser (user)
{
}
export default
{
testCall,
testUser
}
// Vue app
import API from 'util.js';