Vue/Nuxt: How to define a global method accessible to all components?
Asked Answered
D

4

24

I just want to be able to call

{{ globalThing(0) }}

in templates, without needing to define globalThing in each .vue file.

I've tried all manner of plugin configurations (or mixins? not sure if Nuxt uses that terminology.), all to no avail. It seems no matter what I do, globalThing and this.globalThing remain undefined.

In some cases, I can even debug in Chrome and see this this.globalThing is indeed defined... but the code crashes anyway, which I find very hard to explain.

Here is one of my many attempts, this time using a plugin:

nuxt.config.js:

plugins: [
    {
        src: '~/plugins/global.js',
        mode: 'client'
    },
],

global.js:

import Vue from 'vue';
Vue.prototype.globalFunction = arg => {
    console.log('arg', arg);
    return arg;
};

and in the template in the .vue file:

        <div>gloabal test {{globalFunction('toto')}}</div>

and... the result:

TypeError _vm.globalFunction is not a function


Here's a different idea, using Vuex store.

store/index.js:

export const actions = {
    globalThing(p) {
        return p + ' test';
    }
};

.vue file template: test result: {{test('fafa')}}

.vue file script:

import { mapActions } from 'vuex';

export default {

    methods: {
        ...mapActions({
            test: 'globalThing'
        }),
    }
};

aaaaaaaaand the result is.........

test result: [object Promise]

OK, so at least the method exists this time. I would much prefer not to be forced to do this "import mapActions" dance etc. in each component... but if that's really the only way, whatever.

However, all I get is a Promise, since this call is async. When it completes, the promise does indeed contain the returned value, but that is of no use here, since I need it to be returned from the method.


EDIT

On the client, "this" is undefined, except that..... it isn't! That is to say,

console.log('this', this); 

says "undefined", but Chrome's debugger claims that, right after this console log, "this" is exactly what it is supposed to be (the component instance), and so is this.$store!

I'm adding a screenshot here as proof, since I don't even believe my own eyes.

just plain not possible

Denigrate answered 26/8, 2019 at 13:47 Comment(5)
import Vue from 'vue'; Vue.prototype.globalThing = arg => { ... } may you need this?Epicurean
Tried that. didn't work.Denigrate
Plugins is the thing you are looking for. Check nuxt documentation and try again. If that doesnt work, let us see your code somehowFeune
Yep, that's what I thought, but I've been trying to use this plugin feature for a whole afternoon now, and I am no closer to my goal. Code added to reflect the case you described.Denigrate
Is the the documentation you are referring to nuxtjs.org/guide/plugins ? Because this makes no mention of making calls from templates. Then there is the Vue plugins doc, here vuejs.org/v2/guide/plugins.html ... which is really quite divergent from the Nuxt docs. I don't know... maybe I'll just try doing what is described in the Vue docs, anyway.Denigrate
U
30

https://nuxtjs.org/guide/plugins/

Nuxt explain this in Inject in $root & context section.

you must inject your global methods to Vue instance and context.

for example we have a hello.js file.

in plugins/hello.js:

export default (context, inject) => {
  const hello = (msg) => console.log(`Hello ${msg}!`)
  // Inject $hello(msg) in Vue, context and store.
  inject('hello', hello)
  // For Nuxt <= 2.12, also add 👇
  context.$hello = hello
}

and then add this file in nuxt.config.js:

export default {
  plugins: ['~/plugins/hello.js']
}
Ugric answered 14/7, 2020 at 6:12 Comment(0)
M
19
  1. Use Nuxt's inject to get the method available everywhere
export default ({ app }, inject) => {
  inject('myInjectedFunction', (string) => console.log('That was easy!', string))
}
  1. Make sure you access that function as $myInjectedFunction (note $)
  2. Make sure you added it in nuxt.config.js plugins section

If all else fails, wrap the function in an object and inject object so you'd have something like $myWrapper.myFunction() in your templates - we use objects injected from plugins all over the place and it works (e.g. in v-if in template, so pretty sure it would work from {{ }} too).

for example, our analytics.js plugin looks more less:

import Vue from 'vue';
const analytics = {
    setAnalyticsUsersData(store) {...}
    ...
}

//this is to help Webstorm with autocomplete
Vue.prototype.$analytics = analytics;

export default ({app}, inject) => {
    inject('analytics', analytics);
}

Which is then called as $analytics.setAnalyticsUsersData(...)

P.S. Just noticed something. You have your plugin in client mode. If you're running in universal, you have to make sure that this plugin (and the function) is not used anywhere during SSR. If it's in template, it's likely it actually is used during SSR and thus is undefined. Change your plugin to run in both modes as well.

Moose answered 4/9, 2019 at 23:38 Comment(6)
Well, that almost worked. The plugin function is actually found and called, which is a small miracle, but now "this.$store" is undefined in that function. So... it seems that "this" is not what it's supposed to be, ie. the component instance. Finding the store instance is always an idiosyncratic pain in the neck... Why they didn't just make the store a global var, I don't think I'll ever understand. Oh, and good call about making the plugin available server-side. That was very much part of the problem.Denigrate
So this rabbit hole just keeps getting curiouser. So on the server, "this" is undefined, which I guess is normal, and anyway, we wouldn't have any $store there, so who cares. But.... on the client, "this" is also undefined, except that..... it isn't! That is to say, console.log('this', this); says "undefined", but Chrome's debugger claims that, right after this console log, "this" is exactly what it is supposed to be, and so is this.$store! I'm adding a screenshot to the question as proof, since I don't even believe my own eyes.Denigrate
This is only going to be available once the component is created so depends where you're trying to access it - for example, in beforeCreate hook this. will be undefined. The same applies to various Nuxt hooks. Maybe you'll find this series of posts of mine helpful? dev.to/lilianaziolek/…Moose
In this specific case, you can access store by adding it to extract from Nuxt context, that is: export default ({ app, store }, inject)Moose
Also, don't trust commands executed in Devtools context - they may operate in different context than you think. Trust log statements.Moose
This "If it's in template, it's likely it actually is used during SSR and thus is undefined." saved my day.Cyma
T
1

In Nuxt 3, there's a provide property from callback return can achieve this, for example:

plugin file: pusher-js.client.ts

import Pusher from "pusher-js";

export default defineNuxtPlugin((nuxtApp) => {
  const pusher = new Pusher("api_key", {
    cluster: "mt1",
    forceTLS: false,
    httpHost: "127.0.0.1",
    wsPort: 6001,
  });

  return {
    provide: {
      pusher: pusher,
    },
  };
});

component: pages/index.vue

<script setup lang="ts">
const { $pusher } = useNuxtApp();
console.log('🚀 ~ $pusher:', $pusher)

...
</script>

Reference: https://nuxt.com/docs/guide/directory-structure/plugins#providing-helpers

Towline answered 14/1, 2024 at 12:36 Comment(0)
E
-1

This would be the approach with Vuex and Nuxt:

// store/index.js

export const state = () => ({
    globalThing: ''
})

export const mutations = {
    setGlobalThing (state, value) {
        state.globalThing = value
    }
}


// .vue file script

export default {
    created() {
        this.$store.commit('setGlobalThing', 'hello')
    },
};



// .vue file template

{{ this.$store.state.globalThing }}
Ennead answered 26/8, 2019 at 17:58 Comment(1)
Nope. This provides no way to pass a parameter to globalThing, as shown in the question: {{ globalFunction('toto') }}Denigrate

© 2022 - 2025 — McMap. All rights reserved.