22

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

Marc
  • 1,812
  • 4
  • 23
  • 36
  • 1
    `import Vue from 'vue'; Vue.prototype.globalThing = arg => { ... }` may you need this? – talkhabi Aug 26 '19 at 13:49
  • Tried that. didn't work. – Marc Aug 26 '19 at 14:34
  • Plugins is the thing you are looking for. Check nuxt documentation and try again. If that doesnt work, let us see your code somehow – Emīls Gulbis Aug 26 '19 at 14:45
  • 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. – Marc Aug 26 '19 at 14:58
  • Is the the documentation you are referring to https://nuxtjs.org/guide/plugins/ ? Because this makes no mention of making calls from templates. Then there is the Vue plugins doc, here https://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. – Marc Aug 26 '19 at 15:05

3 Answers3

27

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']
}
Amin-Atashzar
  • 285
  • 3
  • 4
18
  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.

Lili
  • 813
  • 2
  • 10
  • 19
  • 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. – Marc Sep 05 '19 at 13:19
  • 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. – Marc Sep 05 '19 at 13:38
  • 1
    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? https://dev.to/lilianaziolek/understanding-nuxt-vue-hooks-and-lifecycle-part-1-48lc – Lili Sep 05 '19 at 13:43
  • In this specific case, you can access store by adding it to extract from Nuxt context, that is: `export default ({ app, store }, inject)` – Lili Sep 05 '19 at 13:46
  • Also, don't trust commands executed in Devtools context - they may operate in different context than you think. Trust log statements. – Lili Sep 05 '19 at 13:47
  • This "If it's in template, it's likely it actually is used during SSR and thus is undefined." saved my day. – chick3n0x07CC Mar 24 '21 at 14:53
-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 }}
eddx
  • 17
  • 2
  • 1
    Nope. This provides no way to pass a parameter to globalThing, as shown in the question: {{ globalFunction('toto') }} – Marc Aug 26 '19 at 18:41