Instance of a Component Available Worldwide

When working with Vue 2.x in our production applications, we utilize a toast component. This toast component is mounted once through a plugin (code provided below) and then added to the Vue prototype for easy access in every component instance.

This setup simplifies things as we no longer need to add the toast component everywhere it's used.

Vue 2.x plugin

export default {
install(vue: any, _: any) {
    const root = new Vue({ render: (createElement) => createElement(Toast) });
    root.$mount(document.body.appendChild(document.createElement("div")));

    const toastInstance: Toast = root.$children[0] as Toast;
    vue.prototype.$toast = {
        show: (state: ToastState, text: string) => { toastInstance.show(state, text); },
        hide: () => { toastInstance.hide(); }
    };
}

This functionality can be called in any component like:

this.$toast.show(ToastStates.SUCCESS, "Some success message");

In a recent project using Vue 3, I wanted to implement something similar. However, due to the lack of access to this in the setup function, the previous approach wasn't feasible.

I explored various options but couldn't find a definitive best practice.

Provide / Inject: Using this method shows promise, where I can do

export const appInstance = createApp(App);

and then

appInstance.provide("toast", toastComponentInstance)

to later inject it into any components. The challenge here is that to make it available in every component, it needs to be attached to the initial app instance before it's created. One workaround could be manually mounting it and passing it in, but that might feel hacky.

Composition: I also came across a related issue discussed here: How to access root context from a composition function in Vue Composition API / Vue 3.0 + TypeScript? which didn't provide much help and required some workarounds to gain access to the plugin. The following code snippet illustrates this:

export function useToast() {

    const root = getCurrentInstance();

    const openToast: (options: ToastOptions) => void = (options: ToastOptions) => {
        root.ctz.$toast.open(options);
    }

    const closeToast: () => void = () => {
        root.ctx.$toast.close();
    }

    return {
        openToast,
        closeToast
    }
}

I have other ideas in mind but they seem somewhat far-fetched and hacky. I would appreciate hearing others' thoughts on alternative solutions. My goal is to find a simple way to have one instance of a toast that can be easily opened or closed whenever needed.

Answer №1

Here is my approach...

I prefer utilizing the Composition API for its ease of passing around internals.

(In this instance, I am opting for a popup over a toast for simplicity)

myPopup.vue


// internal
const popupMessage = Vue.ref('');
const popupVisible = Vue.ref(true);

// external
export const popUpShow = function(message) {
    popupMessage.value = message
    popupVisible.value = true
}
export const popupHide = function () {
    popupVisible.value = false
}

export default {
    setup(){
        return {
            popupMessage, 
            popupVisible,
            popupHide
        }   
    }
}

Some component, anywhere, whether composition or class based...

import { popUpShow } from "./myPopup";

export default {
  methods: {
    myTriggeredEvent() {
      popUpShow("I am your Liter")
    }
  }
}

By exposing popUpShow as a singleton, it can be imported from any location without concern for context.

The potential downside to this structure is limited scalability. If a project grows to a certain size with multiple triggers originating from different sources that require complex logic for state management (although not applicable in this scenario), a managed global store like Vuex would be more suitable.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Tips for efficiently navigating to Vue methods, computeds, and data in VS Code!

I've attempted to utilize the symbol explorer in VSCode (CTRL+P then typing @). It enables me to navigate to methods by jumping to data, but I'm unable to jump to regular properties such as "computed:" or "methods:"? It would come in handy when ...

Using Vue.js to pass an image URL as a prop for CSS animation by converting it to a CSS variable

Need help with a component that features a hover animation displaying 4 rotating images: animation: changeImg-1 2.5s linear infinite; @keyframes changeImg-1 { 0%, 100% { background-image: url('images/wel1.png'); } 25% { background-image: ur ...

Best practices for effectively implementing Vuex getters within the Nuxt Vue Composition API

I have been using @nuxtjs/composition-api(0.15.1), but I encountered some issues when trying to access Vuex getters within computed(). Below is the code snippet using composition API: import { computed, useContext, useFetch, reactive } from '@nuxtjs/ ...

Chorme is throwing a CSRF token mismatch error when working with Laravel API Sanctum

Having an issue with my Vue app and backend Laravel setup. I am using SPA Authentication for authentication. Everything works fine in localhost, but when I deployed it to the server, there are some issues. After sending a login request to sanctum/csrf-co ...

What is preventing me from utilizing require in vue-router's routes.js file?

Vue-router typically requires the standard setup as outlined below: In main.js, the routes.js file is required and it usually contains code similar to this: //routes.js import Register from './components/Register' import Login from './comp ...

Tips for circumventing the use of responsive tables in Buefy

I am facing a challenge with displaying data in a Buefy table in a way that it appears as a conventional table with columns arranged horizontally on all devices, rather than the stacked cards layout on mobile. In order to accommodate the content appropriat ...

Is it possible to assign a Component a name based on data?

In my project, I have three component icons named <DiscoverIcon>, <FeedIcon>, and <ProfileIcon>. In a tab loop, I want to display a different icon for each respective title. I experimented with using a list element like this: { key: 1, ...

Tips on executing the command `npm run build`

I have a VueJS application that I want to deploy in a cPanel. When I try to run npm run build, I encounter the following error: https://ibb.co/0Qn30TX If I run the npm run command, I get this result: https://ibb.co/Xp9bdDp How can I successfully run np ...

Vue: Storing selected list values in an array

I am working on a Vue application where I need to select two elements from a list component and place them inside an array. Currently, I have my list set up with selection functionality thanks to Vuetify. I have bound the selected items to an array using v ...

Customize the theme of Ant Design for VueJS

I have successfully set up my Vue3 application with Tailwind and Ant Design. However, I am facing issues with customizing the Ant Design theme. I have been referring to this guide. When trying to customize the theme, I encountered the following error: Err ...

Incorporating the Revolution Slider jQuery plugin within a Vue.js environment

Currently, my goal is to transform an html project into a vue application. The initial project utilizes a jquery plugin for Revolution slider by including them through script tags in the body of the html file and then initializing them: <script type= ...

Tips for refreshing retrieved data from the server upon page creation in Vue.JS

Upon page load, data is retrieved from the server and displayed in a table format like the one shown below: <tr v-for="option in options"> <td> < ...

Is there a way to implement a targeted hover effect in Vue Js?

I'd like to create a hover button that only affects the nested elements inside it. Right now, when I hover over one button, all the nested elements inside its sibling buttons get styled. Any ideas on how to fix this? <div id="app"> <butto ...

Tips for efficiently loading data into a vuex module only when it is required and addressing issues with async/await functionality

Is there a method to load all the data for a Vuex store once and only load it when necessary? I believe there is, but I am having trouble implementing it. I'm not sure if it's due to my misunderstanding of Vuex or Async/Await in Javascript promi ...

How Vue3 enables components to share props

Having recently made the switch from Vue2 to Vue3, I find myself a bit perplexed about the best approach for sharing props among multiple components. My goal is to create input components that can share common props such as "type", "name", and so on. Previ ...

Make sure to wait for the VueX value to be fully loaded before loading the component

Whenever a user attempts to directly navigate and load a component URL, a HTTP call is initiated within my Vuex actions. This call will set a value in the state once it has been resolved. I am looking to prevent my component from loading until the HTTP ca ...

Multiplying array elements within the Vuex state with the assistance of Socket.io

I have developed an application using Vue and Vuex that connects to a Node/Express backend with Socket.IO to instantly push data from the server to the client when necessary. The data sent to the clients is in the form of objects, which are then stored in ...

A guide on integrating CKEditor's simple upload adapter and resolving the CKEditor error in Vue.js: CKEditorError - duplicated modules

Looking to enhance my Vue project with CKEditor functionality, I successfully integrated the editor but now wish to enable image uploads within the text area. Despite using the simple upload adapter as outlined below, the page displaying the editor is no ...

Utilizing useSeoMeta with Asynchronous Data

I am working on a code snippet that fetches data from my Sanity CMS. My goal is to utilize the useSeoMeta function so that the title corresponds to the title retrieved from Sanity's Data. const { data } = useAsyncData('category', () => sa ...

Using the Tailwind CSS framework in combination with Vue's v-html

My vue component is designed to accept a prop of raw HTML, which originates from a wysiwyg editor utilizing tailwind classes for styling - similar to our vue app. The issue arises when using v-html="responseFromAPI" in my component, as the raw H ...