12

Consider the following composition function:

import { computed, ref } from '@vue/composition-api'
import { isInternetExplorer } from 'src/services/utils/utilsService'
import { Screen } from 'quasar'
import { auth, getAllScopes } from 'src/services/auth/authService'

const isLoginPopup = Screen.lt.sm || isInternetExplorer ? false : true
const accountID = ref('')

export const setAccountID = () => {
  const account = auth.getAccount()

  if (account) { accountID.value = account.idTokenClaims.oid } 
  else { accountID.value = '' }
}

export const useAccount = () => {
  const loading = ref(false)
  const disabled = ref(false)

  const login = async () => {
    loading.value = true
    disabled.value = true

    const allScopes = getAllScopes()

    if (isLoginPopup) {
      try {
        await auth.loginPopup(allScopes)
      } finally {
        setAccountID()
        disabled.value = false
        loading.value = false
      }
    } else {
      auth.loginRedirect(allScopes)
    }
  }

  const logout = () => { auth.logout() }

  return {
    accountID: computed(() => accountID.value),
    isAuthenticated: computed(() => Boolean(accountID.value)),
    loading: computed(() => loading.value),
    disabled: computed(() => disabled.value),
    login, logout,
  }
}

Now when I want to use the isAuthenticated computed property within /router/index.js Vue throws the error "Uncaught Error: [vue-composition-api] must call Vue.use(plugin) before using any function.":

import { route } from 'quasar/wrappers'
import VueRouter from 'vue-router'
import routes from './routes'

import { useAccount } from 'src/comp-functions/useAccount'

export default route(function ({ Vue }) {
  Vue.use(VueRouter)

  const Router = new VueRouter({
    scrollBehavior: () => ({ x: 0, y: 0 }),
    routes,

    mode: process.env.VUE_ROUTER_MODE,
    base: process.env.VUE_ROUTER_BASE,
  })

  Router.beforeEach((to, from, next) => {
    const { isAuthenticated } = useAccount()

    if (isAuthenticated || to.path === '/' || to.path === '/login') {
      next()
    } else {
      next('/login')
    }
  })

  return Router
})

The component API is instantiated by a Quasar Framework boot file in the file '/boot/auth.js':

import VueCompositionApi from '@vue/composition-api'
import { boot } from 'quasar/wrappers'

export default boot(({ Vue }) => {
  Vue.use(VueCompositionApi)
})

Is there a way to use an exported computed property outside of a component?

According to this example, which is a library using the same composition API, it should work when isAuthenticated is instantiated within the Router object. There are more people struggling with this but I can't seem to get it right.

3 Answers 3

32

This problem was fixed by a friend on the Vue.js forum. For anyone else who is running into this issue I'll post his answer here. In short you need to create a separate file that will install the composition API plugin and call that file within the router/index.ts file to instantiate the plugin.

It’s because of the composition API not being inside Vue itself. With the release of Vue 3 this will be fixed.

You need to Vue.use(CompositionApi) before trying to use anything that belongs to @vue/composition-api.

In your main.js or index.js, the app entry point, install it first:

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'

Vue.use(VueCompositionApi)

This works. But I assume your file doesn’t look like this and it looks more like the next one:

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'
import { isAuthenticated } from '@/store/auth'

Vue.use(VueCompositionApi)

This will blow up everything again because the line that installs the composition API (Vue.use(VueCompositionApi)) is after a line that imports something that uses it (import { unauthenticated } from '@/store/auth')

In the meantime, until Vue 3.0 reaches its release, you can create a file that simply installs the plugin:

// installCompositionApi.js

import Vue from 'vue'
import VueCompositionApi from '@vue/composition-api'

Vue.use(VueCompositionApi)

And then in your entry point:

// main.js
import './installCompositionApi.js'
import Vue from 'vue'
import { isAuthenticated } from '@/store/auth'

if (isAuthenticated.value) {
// ...
} else {
// ...
}

This will work.

Sign up to request clarification or add additional context in comments.

2 Comments

I have the exact same error. I see you also use quasar. With quasar you have the boot files. When I follow your instructions and put the install script on top of my boot file I do not get the same result.
Have a look at my repository. Check the boot file and the boot section in quasar.conf.je.
1

Did you install VueCompositionApito correctly?

You must install @vue/composition-api via Vue.use() before using other APIs:
import Vue from 'vue';
import VueCompositionApi from '@vue/composition-api';

Vue.use(VueCompositionApi);

1 Comment

Yes it's installed correctly, and it works in all components except for outside a component. Updated OP with the code.
0

I had the same error , I Just upgrade the version of @vue/composition-api .

1 Comment

Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.