如何使用primeVue toast创建可重用toastService?

ymzxtsji  于 2022-11-17  发布在  Vue.js
关注(0)|答案(6)|浏览(178)

I want to know if there's a way to create a reusable scritp/class/service with primevue toast function calls, in such a way that I don't need to call the primevue toast functions directly in every single component.
What I've tried to do up until now, was to create a ToastService.ts like this:

import { useToast } from 'primevue/usetoast';

    const toast = useToast();

    export function addMsgSuccess(): void {
        toast.add({ severity: 'success', summary: 'Test', detail: 'Test', life: 3000 });
    }

But this code crashes my application and I get the following error:
Uncaught Error: No PrimeVue Toast provided!at useToast (usetoast.esm.js?18cb:8:1) eval (ToastService.ts?73ba:3:1) Module../src/shared/service/ToastService.ts (app.js:1864:1) webpack_require (app.js:849:30) fn (app.js:151:20) eval (cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js?!./node_modules/eslint-loader/index.js?!./src/views/cadastro-plano/CadastroPlano.ts?vue&type=script&lang=ts:31:87) Module../node_modules/cache-loader/dist/cjs.js?!./node_modules/babel-loader/lib/index.js!./node_modules/ts-loader/index.js?!./node_modules/eslint-loader/index.js?!./src/views/cadastro-plano/CadastroPlano.ts?
Does anyone know how to solve this problem, or to create functions that make this add() call, so I don't need to call it everysingle time?

vkc1a9a2

vkc1a9a21#

在试用了一段时间之后,我发现了一个我认为很优雅的解决方案,尽管它使用了Pinia。使用这个方法,您也可以在帮助函数中调用toast,并且重用函数非常简单。

主文件.ts

import { createApp } from "vue";
import App from "./App.vue";

import { createPinia } from "pinia";
import PrimeVue from "primevue/config";
import ToastService from "primevue/toastservice";
import Toast from "primevue/toast";

createApp(App)
    .use(router)
    .use(PrimeVue)
    .use(createPinia())
    .use(ToastService)
    .component("Toast", Toast)
    .mount("#app");

界面.ts

export interface Message {
    severity: string;
    summary: string;
    detail: string;
}

使用通知.ts

import { defineStore } from "pinia";
import { Message } from "@interfaces";

interface State {
    info: Message;
    notify: Message;
    confirm: Message;
}

const useNotifications = defineStore({
    id: "notificationStore",
    // Might be better to only have one piece of state, but this is the solution I went with
    // info for basic notifs, notify for sticky notifs, confirm for notifs that need confirmation
    state: (): State => ({
        info: {
            severity: "",
            summary: "",
            detail: "",
        },
        notify: {
            severity: "",
            summary: "",
            detail: "",
        },
        confirm: {
            severity: "",
            summary: "",
            detail: "",
        },
    }),
});

export default useNotifications;

应用程序版本

<script setup lang="ts">
    import { useToast } from "primevue/usetoast";
    import { useNotifications } from "@store";
    import { Message } from "@interfaces";

    const notificationStore = useNotifications();
    const toast = useToast();

    // Watches changes on notificationStore throughout the app
    notificationStore.$subscribe((mutation, state) => {
        // Checks which part of the state has been mutated, and updates that state based on those conditions
        // mutation.events.key will throw a TypeScript error, but it will still work (until build time where another solution should be found)
        const key = mutation.events.key;
        if (key === "info") {
            const { severity, summary, detail } = state.info;
            toast.add({ severity, summary, detail, life: 3000, group: "br" });
        } else if (key === "notify") {
            const { severity, summary, detail } = state.notify;
            toast.add({ severity, summary, detail, group: "br" });
        } else if (key === "confirm") {
            const { severity, summary, detail } = state.confirm;
            toast.add({ severity, summary, detail, group: "bc" });
        }
    });

    // Use provide to make Toast functionality easily injectable
    provide("toastConfirm", (args: Message) => {
        const { severity, summary, detail } = args;
        notificationStore.confirm = { severity, summary, detail };
    });
    provide("toastNotify", (args: Message) => {
        const { severity, summary, detail } = args;
        notificationStore.notify = { severity, summary, detail };
    });
    provide("toastInfo", (args: Message) => {
        const { severity, summary, detail } = args;
        notificationStore.info = { severity, summary, detail };
    });

    const denyToast = () => {
        toast.removeGroup("bc");
    };
    // Have not figured out how to make this function useable
    const acceptToast = () => {
        toast.removeGroup("bc");
    };
</script>

<template>
    <!-- This group will represent the toasts I do not wish to have to confirm -->
    <Toast position="bottom-right" group="br" />
    <!-- The 'confirmable' toast template is basically copy pasted from PrimeVue docs -->
    <!-- This Toast will appear in the bottom center -->
    <Toast position="bottom-center" group="bc">
        <template #message="slotProps">
            <div>
                <div>
                    <i class="pi pi-exclamation-triangle" />
                    <h4>{{ slotProps.message.summary }}</h4>
                    <p>{{ slotProps.message.detail }}</p>
                </div>
                <div>
                    <div>
                        <Button class="p-button-danger" label="No" @click="denyToast" />
                        <Button class="p-button-success" label="Yes" @click="acceptToast" />
                    </div>
                </div>
            </div>
        </template>
    </Toast>
</template>

任意子组件.vue

<script setup lang="ts">
    import { inject } from "vue";
    import { Message } from "@interface";

    const notifyMe = inject("toastNotify", (args: Message) => {});

    const handleClick = () => {
        notifyMe({
            severity: "success",
            summary: "success!",
            detail: "this is from child component",
        });
    };
</script>

<template>
    <Button @click="handleClick" />
</template>

帮助函数中的用法示例.ts

import { useNotifications } from "@store";

// Database helper function
const send = async (channel: string, data?: Object) => {
    const notificationStore = useNotifications();
    if (data) data = JSON.parse(JSON.stringify(data));

    // response will return something like: { message: "Query failed", error: error } or { message: "Query succeeded", success?: returnData }
    const response = await window.electron.ipcRenderer.invoke(channel, data);
    if (response.error) {
        console.log(response.error);
        notificationStore.notify = {
            severity: "danger",
            summary: "Error",
            detail: response.message,
        };
    } else {
        notificationStore.info = {
            severity: "success",
            summary: "Success",
            detail: response.message,
        };
        if (response.success) return response.success;
    }
};

export default send;
jpfvwuh4

jpfvwuh42#

对于答案:above@match,对于那些正在挣扎的人,请将App.vue更改为:

<template>
       <div id="app"></div>
       <Toast />
  </template>
    
  <script>
    import { inject } from 'vue'
    import { useToast } from 'primevue/usetoast'
    
    export default {
       setup() {
          const toastBus = inject('toastBus')
          const toast = useToast()
    
          toastBus.on('*', (type, args) => {
          if (type === 'add') {
            toast.add(args)
          } else if (type === 'remove') {
            toast.remove(args)
          }
       })    
    }
  }

</script>
3qpi33ja

3qpi33ja3#

针对需要在 js 文件中使用吐司的用户的示例

// App.vue

        <template>
            <Toast />
        </template>
        
        <script>
        import Toast from "primevue/toast";
        
        export default {
          components: {
            Toast: Toast,
          },
        </script>
// store.js

        import { useToast } from "primevue/usetoast";
        
        export const StoreExample = defineStore("StoreExample", () => {
          const toast = useToast();
    
          const onActionGetProducts = async () => {
             return API_EXAMPLE.onApiGetProducts().then(() => {
    
               toast.add({
                  severity: "success",
                  summary: "Success Message",
                  detail: "Order submitted",
                  life: 3000,
               });
             });
          };
    
         return {
           onActionGetProducts,
         };
       });
lokaqttq

lokaqttq4#

也许下面的解决方案对您有效:
在App.vue中添加一个吐司,并添加一个查看来自商店的消息的手表

<template>
  <router-view />
  <Toast position="bottom-right" group="br" />
</template>
<script>
import { watch } from "vue";
import { useStore } from "vuex";
import { useToast } from "primevue/usetoast";
export default {
  setup() {
   const store = useStore();
   const toast = useToast();

    watch(
      () => store.getters.getErrorMessage,
       (message, prevMessage) => {
        console.log("error message", message);
        if (message) {
          toast.add({
            severity: "error",
            summary: "Error",
            detail: message,
            group: "br",
            life: 6000,
          });
        }
      }
    );
  },
};
</script>

商店

import { createStore } from "vuex";

export default createStore({
    state: { errorMessage: "" },
    mutations: {        
        setErrorMessage(state, payload) {
            state.errorMessage = payload;
        },
    },
    actions: {},
    modules: {},
    getters: {   
            getErrorMessage: (state) => state.errorMessage,
    },
});

然后,在任何其他组件中只更新消息

store.commit("setErrorMessage", error.message);
6fe3ivhb

6fe3ivhb5#

这个解决方案对我很有效,但我不确定它是不是一个好的解决方案。
首先:从main.ts导出应用程序

// main.ts
import {createApp} from 'vue';
import App from '@/App.vue';

import PrimeVue from 'primevue/config';
import ToastService from 'primevue/toastservice';

export const app = createApp(App);

app.use(PrimeVue);
app.use(ToastService);

app.mount('#app')

第二:导入应用程序并使用app.config.globalProperties的吐司服务

// myToastService.ts
import {ToastSeverity} from 'primevue/api';
import {app} from '@/main';

const lifeTime = 3000;

export function info(title: string = 'I am title', body: string = 'I am body'): void {
  app.config.globalProperties.$toast.add({severity: ToastSeverity.INFO, summary: title, detail: body, life: lifeTime});
};

export function error(title: string = 'I am title', body: string = 'I am body'): void {
  app.config.globalProperties.$toast.add({severity: ToastSeverity.ERROR, summary: title, detail: body, life: lifeTime});
};

第三步:在组件中导入myToastService。

// myTestToastComponent.vue
<script setup lang="ts">
import {info, error} from '@/components/myToastService';

info();
</script>
w1e3prcc

w1e3prcc6#

另一种选择是在单个位置(App.vue)处理toast,然后通过消息总线接收它们。
首先在main.js中设置总线:

import mitt from 'mitt'
const toastBus = mitt()

const app = createApp(App)
// Provide the bus for injection for use by components
app.provide('toastBus', toastBus)

// Export the bus for use by 'non-component' scripts
export { toastBus }

然后将总线注入App.vue,侦听事件并生成Toast:

<template>
<div id="app"></div>
<Toast />
</template>

<script>
import { inject } from 'vue'
import { useToast } from 'primevue/usetoast'

const toastBus = inject('toastBus')
const toast = useToast()

toastBus.on('*', (type, args) => {
  if (type === 'add') {
    toast.add(args)
  } else if (type === 'remove') {
    toast.remove(args)
  }
})

然后再送上一份分量的吐司词:

let toastBus = inject('toastBus')

toastBus.emit('add', { severity: 'success', summary: 'Test', detail: 'Test', life: 3000 })

或从非组件代码:

import { toastBus } from './main'

toastBus.emit('add', { severity: 'success', summary: 'Test', detail: 'Test', life: 3000 })

相关问题