Issue
Updated from my original question. The content of the question remains the same, i only updated the scope. I am looking forwdard to call my Auth store, which calls a composable, which calls i18n.
The problem begins when i try to call my Auth Store from the router.beforeEach
callback.
I can freely use this composable, except for the usecase inside the router guard callback.
router.ts
import { createRouter, createWebHistory } from '@ionic/vue-router'
import routes from '~pages'
const router = createRouter({
history: createWebHistory(import.meta.env.BASE_URL),
routes,
})
router.beforeEach((to) => {
const publicPages = ['/login']
const authRequired = !publicPages.includes(to.path)
const auth = useAuthStore()
if (authRequired && !auth.auth) {
auth.returnUrl = to.fullPath
return '/login'
}
})
export default router
main.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import { IonicVue } from '@ionic/vue'
import { defineCustomElements } from '@ionic/pwa-elements/loader'
import { createI18n } from 'vue-i18n'
import messages from '@intlify/unplugin-vue-i18n/messages'
import App from './App.vue'
import router from '~/modules/router'
// Omited several CSS imports
import 'uno.css'
defineCustomElements(window)
const pinia = createPinia()
const i18n = createI18n({
legacy: false,
locale: 'en',
messages,
})
const app = createApp(App)
.use(IonicVue)
.use(i18n)
.use(pinia)
.use(router)
router.isReady().then(() => { app.mount('#app') })
Yet i am seeing this error: Uncaught SyntaxError: Must be called at the top of a setup function
My Pinia store is a as simple as an authentication store would look like:
import { acceptHMRUpdate, defineStore } from 'pinia'
import type { Auth } from '~/types'
export const useAuthStore = defineStore('auth', () => {
const { success, error, warn } = useToastManager()
const auth = ref<Auth | null>(JSON.parse(localStorage.getItem('auth')!) ?? null)
const returnUrl = ref('')
const { httpClient } = useHttpClient()
const isLogin = computed(() => {
return auth.value !== null
})
function login(username: string, password: string): Promise<boolean> {
isLoading.value = true
return new Promise((resolve, reject) => {
httpClient.post<Auth>('/api/Account/Login',
{
username,
password,
}).then(async (response) => {
auth.value = response.data
localStorage.setItem('auth', JSON.stringify(response.data))
success('Login', 'Silent')
resolve(true)
}).catch(async (e) => {
error('Login', e, e.code)
})
})
}
async function logoff() {
auth.value = null
}
return { login, logoff, isLogin, auth, returnUrl }
})
if (import.meta.hot)
import.meta.hot.accept(acceptHMRUpdate(useAuthStore, import.meta.hot))
Here is my composable for managing toasts.
import { toastController } from '@ionic/vue'
import { alertCircleOutline, checkmarkCircleOutline, helpCircleOutline } from 'ionicons/icons'
import { useI18n } from 'vue-i18n'
const errorTimeout = 3000
const successTimeout = 1000
export function useToastManager() {
const { t } = useI18n()
const success = async (action: string, message: string) => {
isLoading.value = false
const toast = await toastController.create({
message: t('success'),
duration: successTimeout,
position: 'bottom',
color: 'success',
icon: checkmarkCircleOutline,
})
if (message === 'Silent')
return
if (action === 'Fetch')
return
await toast.present()
}
const error = async (action: string, message: string, code?: string) => {
isLoading.value = false
let showMessage = ''
switch (code) {
case 'ERR_NETWORK':
showMessage = t('network_error')
break
case 'ERR_BAD_RESPONSE':
showMessage = t('invalid_operation')
break
case 'ERR_BAD_REQUEST':
showMessage = t('invalid_operation')
if (action === 'Login')
showMessage = t('err')
break
case 'ECONNABORTED':
showMessage = t('timeout_failed_operation')
break
default:
showMessage = 'No err code found'
}
const toast = await toastController.create({
message: showMessage,
duration: errorTimeout,
position: 'top',
color: 'danger',
icon: alertCircleOutline,
// onclick: detailedAlert(action, message)
})
await toast.present()
}
const warn = async (action: string, message: string) => {
isLoading.value = false
const toast = await toastController.create({
message: `${message}`,
duration: errorTimeout,
position: 'bottom',
color: 'warning',
icon: helpCircleOutline,
})
await toast.present()
}
return { success, error, warn }
}
Solution
It is probably caused by calling const { t } = useI18n()
in the composable. i18n is complaining that you call the use-function not from a setup function: 'Uncaught SyntaxError: Must be called at the top of a setup function'.
In a large project where there we use options and composition api, we use the global t
function as a work-around.
Make a i18n module i18n.ts
:
import { createI18n } from 'vue-i18n'
import messages from '@intlify/unplugin-vue-i18n/messages'
export const i18n = createI18n({
legacy: false,
locale: 'en',
messages,
})
Import this in your main.ts
and call it like you in your example (.use(i18n)
).
Then in your composable, import the i18n module and get the global t function from it:
import { i18n } from '..../i18n'; // import your i18n.ts file
const { t } = i18n.global;
Answered By - Gabe
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.