I'm currently working on a vue 3 app using primevue 3.46. I'm wondering if it possible to use the ToastService to display a toast outside of a vue component.
Here is an example. I created an Axios instance so that i can intercept request and response errors. I would like to display a Toast if the status code is 401 (Unauthorized) or 403 (Forbidden). Tha fact that my axios interceptor is outside a vue component make it impossible to use the toast service because it provides a method for the Composition or the Option API.
Here is my main.ts file
// Core
import { createApp } from "vue";
import App from "@/App.vue";
// Styles
import "primevue/resources/themes/lara-dark-blue/theme.css";
import "primeflex/primeflex.css";
import "primeicons/primeicons.css";
// Plugins
import PrimeVue from "primevue/config";
import ToastService from "primevue/toastservice";
const app = createApp(App);
app.use(PrimeVue, { ripple: true });
app.use(ToastService);
app.mount("#app");
Here is my plugin/axios.ts file
import axios, { AxiosError } from "axios";
import { useToast } from "primevue/usetoast";
// Set default axios parameters
const instance = axios.create({
baseURL: import.meta.env.VITE_API_BASE_URL,
});
instance.interceptors.response.use(
(response) => response,
(error: AxiosError) => {
const toast = useToast(); // The error seems to be here
if (error.response?.status === 401) {
toast.add({ summary: "Unauthenticated", severity: "error" });
} else if (error.response?.status === 403) {
toast.add({ summary: "Forbidden", severity: "error" });
} else {
return Promise.reject(error);
}
}
);
export default instance;
The problem is that is always returns a vue error : [Vue warn]: inject() can only be used inside setup() or functional components.
Is there any way to use the ToastService outside a vue component like in an axios interceptor ?
You can only use
useToast()(or any other composable function) inside thesetupfunction of a Vue component (or inside its<script setup>, which is the same thing), or inside another composable function (because it has the same context limitation).But you can use a store (e.g: a reactive object, a pinia store, etc...) to trigger a change from the interceptor and then
watchthis change from a component responsible for rendering the alerts.Generic example:
toastStore.ts
axios.ts
App.vue 1:
If you use
<script setup>inApp.vuejust place the call touseToastStore()inside the setup script.The above uses a rather basic reactive object as store. If you already have a store in your app, you can use its state to store the toasts array. 2
1 - You don't have to place it in
App.vue, you can use any other component, as long as its mounted when you push toasts to the store, so they get rendered.2 - Note the code above will only show toasts pushed to the store while the rendering component (the one calling
useToastStore) is mounted.If you want a different behavior, you need to track which toasts have been shown to the user and which haven't, so you don't render the same toast more than once. That's why I suggested calling
useToastStoreinApp.vue.