Vue + Meteor Accounts

@aadams you were asking about Meteor Accounts in Vue, so here’s my take on the matter, using accounts-password package without Blaze templates and passing the user to Vuex.

It’s less complicated than in my own stuff (no validation etc and without Jade so more people can learn from it), but it does its job. Most probably few things could be done better and I’m counting on you guys to point them out.

How do you think, what should be the best practices for accounts management in Vue? How do you organize it in your projects?

One more thing, the project uses Vuex2 from npm, not Akryum’s package which is still based on Vuex1. That will most probably change when Vuex2 package is ready.

7 Likes

Thanks for this @gusto. I haven’t looked at the project yet, but have a quick question. How are you integrating with the Accounts package when you don’t have the Accounts package installed in your project? Is there an alternative API that allows you to hook into it?

I’ve got accounts-password package, which automatically installs basic accounts package as a dependency.

Oh, doesn’t that package have a dependency on Blaze?

No, only accounts-ui has dependency on Blaze. accounts-password just provides a set of methods to register/login with password.

1 Like

Okay, that clears it up for me! :slight_smile:

Once we have the API via accounts-password, we just need to make the appropriate calls and build the access forms in order to integrate with it. It’s more work to be sure, but doable!

More work, but worth it. :slight_smile: I’ve been doing it with Blaze anyways, because accounts-ui doesn’t satisfy my needs, f.e. doesn’t check if the user / email is available.

that must be in config somewhere. For life of me I can’t get Meteor.user() into vue data space or somewhere near that other than blunt {{Meteor.user()}} in template. Meteor.user() is recognized by akryum-vue, but it won’t overwrite on top of vue data as if doing user.find() {which won’t work}.

The Vuex action below works for me. I call it in the created hook of my main component. I commit to three properties - whole user object, string with username or guest and string with user Id

  fetchMeteorUser({commit}) {
    Tracker.autorun(() => {
      const user = Meteor.user()
      commit('updateCurrentUser', user)
      commit('updateUsername', user ? user.username : 'guest')
      commit('updateUserId', user ? user._id : null)
    })
  }
1 Like

So something like this wouldn’t work be it getter or state. So you literally would have to use mapState/Getters.

  computed: {
    chars() {
      if(this.$store.userGet) {
        console.log("yes")
        return this.$store.userGet
      }
    }
  },

and then you genuinely need to commit the props that you need since trying to access them from singleton state will break the app.

I’m not sure what the content of your this.$store.userGet is.

Probably, I should rewrite my methods so that it only commits user object and add getters for username and userId. But I got used to this structure so for now I keep it as it’s working fine.

Apparently that breaks app if state.user is undefined when you state.user.prop in getters, so you really did the right thing (at very least on first load it will break). I made issue of it here (that Meteor.user() is difficult to access): https://github.com/Akryum/meteor-vue-component/issues/118

1 Like

@janat08 I did some testing, tweaked a few things and here are a few solutions:

  1. a mixin
import { Meteor } from 'meteor/meteor'

export default {
  meteor: {
    meteorUser() {
      return Meteor.user() ? Meteor.user() : {}
    }
  }
}
  1. Vuex with state
  fetchMeteorUser({commit}) {
    Tracker.autorun(() => {
      commit('updateMeteorUser', Meteor.user() ? Meteor.user() : {})
    })
  }
  1. Vuex with getter
  meteorUser: state => state.meteorUser ? state.meteorUser : {},

That said, it’s a common situation that we want a user object to contain more fields that just what Meteor.user() returns, so we get it from additional publication.

1 Like

Okay I responded on github about this.

So how do you actually access subscription if “this” isn’t available in component router hook. I was told to use the global one by Akryum, but then should I just call the subscriptions in there instead then?

Here’s a little trick allowing to wait until Meteor.user() is available in vue-router navigation guards.

This can be used either to restrict the access for guests or to make sure that the component doesn’t get created before Meteor.user() is available. That second use case can be also done by v-if on <router-view> though.

If used on the parent route level, it automatically covers all the children routes.

If there is such a need, it can be even used to make whole Vue instance in /client/main.js wait until Meteor.user() is ready, but I am not sure if that is a good practice. Potentially it can be useful with Meteor 1.5’s dynamic imports.

BeforeEnter hook and next() function are explained in the navigation guards chapter of vue-router documentation.

/imports/api/routes/someRoutesModule.js

import isMeteorUser from '/imports/api/helpers/isMeteorUser'
import SomePageComponent from '/imports/ui/pages/SomePageComponent.vue'

export default [
  {
    path: '/somepath',
    component: SomePageComponent,
    beforeEnter: (to, from, next) => {
      isMeteorUser().then(response => {
        // if true, continue, else redirect to Login page
        response ? next() : next({name: 'Login'})
      })
    }
  }
]

/imports/api/helpers/isMeteorUser.js

import { Meteor } from 'meteor/meteor'
import { Tracker } from 'meteor/tracker'

export default () => {
  return new Promise((resolve, reject) => {
    Tracker.autorun((c) => {
      // stop computation when Meteor.user() is ready
      Meteor.user() !== undefined && c.stop()
      // return false if user is a guest
      Meteor.user() === null      && resolve(false)
      // return true if user is logged in
      Meteor.user()               && resolve(true)
    })
  })
}
6 Likes

this is perfect, thanks for saving me the boilerplate :thumbsup: