Paul Carlson
Paul Carlson

Reputation: 457

vue3 creating global variables returns undefined

I am currently working on upgrading a project to Vue3. Inside the project we have several files that create global variables inside a boot directory:

src/boot/auth.js
src/boot/axios.js
src/boot/cranky.js
....

Each of these files creates global variables that I am using throughout the project. For example my auth.js file:

import auth from '../app/auth';
import { createApp } from 'vue';



export default async ({
    router,
    Vue
}) => {
    const app = createApp({});
app.config.globalProperties.$auth = auth;
    // Vue.prototype.$auth = auth;
    //This works with logic in MainLayout to permit users to see particular screens
    //after they are logged in.
    router.beforeResolve((to, from, next) => {
        if (to.matched.some(record => record.meta.requiresAuth)) {
            // eslint-disable-next-line no-unused-vars
            let user;
            if (app.config.globalProperties.$auth.isAuthenticated()) {
                // TODO: record screens viewed by router url
                next();
            } else {
                next({
                    path: '/home'
                });
            }
        }
        next();
    });
};

After research and reading I understand that global variables are created like this: app.config.globalProperties.$auth = auth;, Which then should be called from other component files using this.$auth. In my case, however, this returns as undefined.

My theory is that there is an issue with my createapp. Am I calling that correctly, or am I missing something else?

Thanks.

EDIT

added code requested by @tony19 The following the full script tag. My understanding and I probably am wrong, is that the this works as a global in vue3.

<script>
import HeaderScoreIndicator from '../components/HeaderScoreIndicator.vue';
import ExitResearchControl from '../components/ExitResearchControl.vue';
import {mq} from 'src/boot/mq'

export default {
    components: {
        HeaderScoreIndicator,
        ExitResearchControl
    },
    data: function () {
        return {
            tab: 'denial',
            signedIn: false,
            username: null,
            user: null
        };
    },
    computed: {
        /*
            These 4 functions bind the colors to the background elements
        */
        title: function () {
            return this.$store.state.globals.title;
        },
        toolbarStyle: function () {
            return 'padding-top: 10px; background-color: ' + this.$store.state.globals.toolbarColor;
        },
        footerStyle: function () {
            return `background-color: ${this.$store.state.globals.toolbarColor};`;
        },
        backgroundStyle: function () {
            if(!this.$mq.phone){
                return `background: linear-gradient(180deg, ${this.$store.state.globals.backgroundGradientTop} 0%,  ${this.$store.state.globals.backgroundGradientBottom} 100%);
                display: flex;
                justify-content: center;`;
            } else{
                return `background: linear-gradient(180deg, ${this.$store.state.globals.backgroundGradientTop} 0%,  ${this.$store.state.globals.backgroundGradientBottom} 100%);`;
            };
        },
        limitSize: function(){
            if(!this.$mq.phone){
                return 'max-width: 1023px; width: 100%;'
            } else{
                return
            };
        }
    },
    /*
        In the beforeCreate function, we're setting up an event listener to detect
        when we've logged in.  At the successful login we can push the user to
        the correct screen.
    */
    beforeCreate: function () {
        console.log(this.$auth);
        return this.$auth.getState().then(data => {
            if (this.$auth.isAuthenticated()) {
                this.username = this.$auth.getEmail();
                return this.initialize(data).then(() => this.signedIn = true);
            } else {
                this.signedIn = false;
                return newUserLanguageSelection()
            }
        }).catch(e => {
            this.signedIn = false;
            console.error(e);
        });

    },
    methods: {
        newUserLanguageSelection: function(){
            if(localStorage.getItem('languageSet') != 'true'){
                    return this.$router.push('/language');
            }
        },
        initialize: function (data) {
            this.$store.commit('globals/dataLoaded');
            this.$store.commit('scoring/setUsername', this.username);
            return this.$store.dispatch('scoring/initializeScoring', { points: data.score | 0 })
                .then(() => {
                    if(this.$store.state.globals.testQuiz){
                        return;
                    } else if(localStorage.getItem('languageSet') != 'true'){
                        return this.$router.push('/language');
                    }else if (data.seenAnalyticsDialog == true) {
                        return this.$router.push('/home');
                    } else {
                        return this.$router.push('/consent');
                    }
                })
                .catch(e => {
                    //silence NavigationDuplicated errors
                    if (e.name != "NavigationDuplicated")
                        throw e;
                });
        }
    }
};

</script>

Upvotes: 0

Views: 4495

Answers (1)

tony19
tony19

Reputation: 138276

In the previous Vue 2 project, it looks like Vue was passed to the auth.js module so that it could attach $auth to the Vue prototype.

However, in an attempt to upgrade that particular code in the auth.js module to Vue 3, you ignore the Vue argument, and create a throwaway application instance to create the $auth global property:

// src/boot/auth.js
export default async ({
    router,
    Vue,
}) => {
    const app = createApp({}); āŒ local app instance
    app.config.globalProperties.$auth = auth;
    ā‹®
}

But that won't work because the app instance is not somehow hooked into the application instance you'll eventually mount in main.js.

Fixing the upgrade

Your main.js probably looks similar to this:

// src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import auth from './boot/auth';

auth({ router, Vue });
ā‹®
Vue.createApp(App).mount('#app');

To correctly upgrade the original code, you should pass the app instance from main.js to auth.js:

// src/main.js
import { createApp } from 'vue';
import App from './App.vue';
import router from './router';
import auth from './boot/auth';

const app = createApp(App);

auth({ router, app });
ā‹®
app.mount('#app');

And use that app in the auth.js module:

// src/boot/auth.js
export default async ({
    router,
    app, šŸ‘ˆ
}) => {
     šŸ‘‡
    app.config.globalProperties.$auth = auth;
    ā‹®
}

Convert to Vue plugin

Assuming you don't need to await the return of the auth.js module, you could make that module a Vue plugin instead, which automatically receives the app instance as the first argument. The second argument receives the options passed in from app.use():

// src/boot/auth.js
export default {
  install(app, { router }) {
    app.config.globalProperties.$auth = auth;

    router.beforeResolve(ā‹Æ);
  }
}
// src/main.js
import Vue from 'vue';
import App from './App.vue';
import router from './router';
import auth from './boot/auth';

const app = createApp(App);
app.use(auth, { router });
ā‹®
app.mount('#app');

Upvotes: 2

Related Questions