Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a single instance of a class within a Vue application

I'm new to Vue and I'm struggling to wrap my head around how to implement what seems to me like a good case for a global variable or singleton.

The background is that I'm using Azure AD B2C for authentication with the MSAL library. MSAL requires a single instance of the Msal.UserAgentApplication to be declared and then shared through the application.

What I'm struggling with is how to declare that instance somewhere central and then access it from each component including the router.

At the moment I've got a class which is similar to this example: https://github.com/sunilbandla/vue-msal-sample/blob/master/src/services/auth.service.js and when I want to use the methods I'm doing:

var authService = new AuthService();
authService.Login();

Unfortunately this creates a new instance of MSAL each time the class is instantiated which in turn caused my users to end up stuck in an authentication loop.

Any help would be greatly appreciated.

Many thanks.


Following on from the answer below by Teddy I've amended my main.js as follows:

import Vue from 'vue'
import App from './App.vue'
import router from './router'
import store from './store'
import './registerServiceWorker'
import AuthService from './services/AuthService';

Vue.config.productionTip = false

Vue.prototype.$authService = new AuthService();

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount('#app');

And my register.vue component as follows:

<template>
  <div class="about">
    <h1>This is the register page, it should redirect off to B2C</h1>
  </div>
</template>

<script>
  import router from '@/router.js'

  export default {

    created(){

      this.$authService.isAuthenticated().then(
      function(result){
        if(result){
          router.push('/');  
        }
        else{
          authService.register();  
        }
      });
    }  
  } 
</script>

The component is saying that this.$authService is undefined so it's obviously not reading the prototype.

It feels like I'm missing something really fundamental in Vue at this point.

like image 357
AndyM Avatar asked Oct 23 '18 08:10

AndyM


People also ask

Can you have multiple Vue instances?

It is totally legal and fine to have multiple Vue instances on your page controlling different parts and pieces of it. In fact, you can even have your instances communicate with each other by storing them in variables within the global namespace.

What is an instance Vue?

A Vue instance uses the MVVM(Model-View-View-Model) pattern. The Vue constructor accepts a single JavaScript object called an options object. When you instantiate a Vue instance, you need to pass an options object which can contain options for data, methods, elements, templates, etc.

Is VUEX a singleton?

State. If you are using Vuex, then you will have only one Single store for each VueJS powered application. Vuex Store is Singleton source of State. Our store contains application state.


1 Answers

You can just add it as a Vue instance property. It will be there for all Vue components.

Set it up in main.js like this:

Vue.prototype.$authService = new AuthService();

You can later access it in any Vue component. For example:

this.$authService.Login();

Refer: https://vuejs.org/v2/cookbook/adding-instance-properties.html

Edit: You have to use this.$router.push and this.$authService.register inside the isAuthenticated callback. If "this" refers to something else in that block, store var self=this; before the callback starts, or use fat arrow syntax.

<script>
  //No import as router is available in 'this'
  export default {

    created(){
      var self=this; //For use inside the callback
      this.$authService.isAuthenticated().then(
      function(result){
        if(result){
          self.$router.push('/');  
        }
        else{
          self.$authService.register();  
        }
      });
    }  
  } 
</script>

Edit 2:

Maybe you can create the instance (singleton) itself in a file called AuthServiceInst.js. Then you can import it in both main.js and router.js.

New file AuthServiceInst.js:

import AuthService from './AuthService.js'

export const authService = new AuthService();

main.js:

import {authService} from './AuthServiceInst.js'

Vue.prototype.$authService = authService;

router.js:

import {authService} from './AuthServiceInst.js'

//Now you can use authService
like image 144
Teddy Avatar answered Sep 22 '22 04:09

Teddy