Using vue-meteor-tracker to update Meteor.user does not work

I am new to both Vuejs and Meteorjs so please excuse if these are newbie questions or the syntax is off.
I am trying to use vue-meteor-tracker to update the login\register button to logout but I am unable to do so. The Meteor.user always comes as undefined.
What am I missing?

base.js:

import { Meteor } from 'meteor/meteor';
export default {
  meteor: 
  {
    $lazy: true,
    $subscribe: 
    {
      'currentUser': function() {
        return Meteor.user();
      },
      'currentUserId': function() {
        return Meteor.userId();
      }
    },
  },
}

plugins.js

import VueMeteorTracker from 'vue-meteor-tracker';
Vue.use(VueMeteorTracker);

MainNavBar.vue

<li v-if="($parent.currentUserId==null)">
  <router-link v-bind:to="{ name: 'register' }">Register</router-link></li>
<li v-else>
  <router-link v-bind:to="{ name: 'register' }">Log out</router-link></li>
<script>
import base from '../base';
export default 
{  
  extends: base,  
  components:{
  },
  mounted() 
  {
    console.log(this.$parent.currentUserId);
  }
}
</script>

Login.Vue:

<script>
import '../api/methods.js';
import { Meteor } from 'meteor/meteor';
import base from './base';
export default
{
extends: base,
methods: 
  { 
    LoginUserForDomain() 
    {
      Meteor.call('loginUserForDomain', this.user.email, this.user.password, this.user.domain, 
        (error, result) => 
        {
          if(result && result.userId && result.domain) 
          {                 
            Meteor.loginWithPassword(email, password);
            this.$router.push({ name: 'dashboard', params: { domain: result.domain }});                   
            return;
          }
        }
        );
    },
  },

In base.js you are $subscribing to currentUser and currentUserId instead of defining them as reactive data. Try this instead:

import { Meteor } from 'meteor/meteor';
export default {
  meteor: 
  {
    $subscribe: 
    {
      'currentUser': function() {
        // You need to get the data from the server, unless you are using autopublish, which you shouldn't be!
        // passing Meteor.userId() to the subscription makes the subscription change when a user logs in/out
        // (but DONT TRUST THIS within the server publication, always use this.userId instead)
        // Subscription parameters need to be returned in an array
        return [Meteor.userId()];
      },
    },
    // now define the reactive properties directly in the meteor object, not inside the subscribe object
    'currentUser': function() {
      return Meteor.user();
    },
    'currentUserId': function() {
      return Meteor.userId();
    }
  },
}

Also, I’m not familar with using extends and accessing parent data with this.$parent.

The officially recommended way to share this data is to use Vuex. However, since we already have minimongo I don’t like to install Vuex just to share this type of data. Personally, I use my main root component as my shared data store (e.g. do all the above in App.vue), then I can access it from anywhere with this.$root.currentUser without having to import anything into every component which needs it.

Also, note that v-bind: and v-on: can be written much more quickly (and make your templates easier to read) as just : and @. See here: https://vuejs.org/v2/guide/syntax.html#Shorthands

Thank you @wildhart. i will try this out.
Also thank you for the "use my main root component as my shared data store (e.g. do all the above in App.vue), then I can access it from anywhere with this.$root.currentUser" comment. I learnt something new today.

I made the required changes and the the vue-meteor-tracker does not seem to be updating the TopNavBar component after the user has logged in. The TopNavBar is however updated when the user does a browser refresh(after the user has logged in).

Also when a new user registers, the vue-meteor-tracker does not seem to be updating the TopNavBar component. Compared to the login functionality, the system registers the user at the server side instead of the vuejs client side. How would I handle the component communication in this scenario? i am thinking that it might be easier and more reliable to use Vuex(or $on and $emit) for inter-component instead of vue-meteor-tracker.

Registeruser.vue

RegisterUser() 
    {
      Meteor.call('createUserForDomain', this.user.email, this.user.password, this.user.domain, function(error, result)
      {
        if(result && result.userId && result.domain ) 
      {
          this.$router.push({ name: 'dashboard', params: { domain: result.domain }});                  
          return;
        }
        this.failureMessage='There was an error registering your domain. Our administrators have been notified of the issue and we will have a look.';
        return;
      }.bind(this));
    },

I was able to get the application working by using a global event bus. Thank you @wildhart for your inputs.

Hi @ajitgoel, I saw your post on the Vue forum as well.

I’m glad you’ve got it working, but it should not be necessary to use events for this. If you setup vue-meteor-tracker properly, and create reactive properties in your $root Vue instance (or any shared store) which automatically update whenever a user logs in or out, then any other component should be able to use those properties reactively.

If it’s not working, then we need to see more code to find the problem. If you don’t want to publicly share your code then feel free to send me a zip by PM and I’d happily take a look.

Thank you @wildhart for your response. I have shared the source code at:
https://github.com/ajitgoel/commonmembership . This code is based on the following github.

I have removed vuejs EventBus and am currently trying to make the Vuejs components communicate using Meteorjs subscriptions\publications. Here is some code that I had not shared previously.

$\src\imports\server.js:

import { Mongo } from 'meteor/mongo';
import { Meteor } from 'meteor/meteor';

  Meteor.publish('currentUserId', function () 
  {
    return this.userId();
  });

$\src\imports\api\methods.js:

import { Meteor } from 'meteor/meteor';
DomainExistsAlready=-1;
Meteor.methods(
{
  createUserForDomain(email, password, domain) 
  {
    if(Meteor.isServer)
    {
      const { userService } = require('../server/users.js');
      return userService.createUserIfItDoesNotExist(email, password, domain);
    }
  }, 
  loginUserForDomain(email, password, domain) 
  {
    if(Meteor.isServer)
    {
      const { userService } = require('../server/users.js');
      return userService.loginUserForDomain(email, password, domain);
    }
  },
})

$\src\imports\server\users.js:

'use strict';
import { check } from 'meteor/check';
import { Meteor } from 'meteor/meteor';
import { Accounts } from 'meteor/accounts-base';
//export const Users = new Mongo.Collection('users');
export const Users = Mongo.Collection.get('users');

export const userService = 
{
  createUserIfItDoesNotExist(email, password,domain) 
  {    
  },
  loginUserForDomain(email, password, domain) 
  {
  },
}

Can you also commit to github your .meteor folder - all files which don’t start with a . so that I can recreate your meteor environment. Don’t commit the local folder or the .* files.

Also:

  • it is not necessary to publish the logged-in user’s userId - this is always available on the client. It is reactive and you can convert it to a Vue reactive property using vue-meteor-tracker.
  • within a publication use this.userId instead of this.userId().
  • Unless you a using the autopublish package (which you shouldn’t be), then you will need to publish the logged-in user’s data.

Hello @wildhart, i have checked in the .meteor folder.

This definitely seems to be an issue around the subscribe code(using vue-meteor-tracker) that I had added to App.Vue file. After I have logged in, in my debugger on the client, I see this.$root.currentUserId is null but Meteor.userId() has the correct value. Since Meteor.userId() is reactive, I have removed the subscription code and am logging out\login users based on Meteor.userId().

app.vue:

<script>
import { Meteor } from 'meteor/meteor';
export default {
  meteor: 
  {
    currentUserId()
    {
      return Meteor.userId();
    }
  },
}
</script>

mainVavBar.vue:

<li class="nav-item d-lg-none d-xl-block" v-if="(this.$root.currentUserId==null)">
            <router-link class="nav-link" v-bind:to="{ name: 'register' }">Register</router-link>
          </li>
          <li class="nav-item d-lg-none d-xl-block" v-else>
            <a class="nav-link" href='' v-on:click="return Logout()">Log out</a>
          </li>

So you have it working correctly now? I’m only just pulling your last commit to take a look, but if you’ve figured it out then great!

btw, you don’t need this. within Vue templates:
v-if="(this.$root.currentUserId==null)"
can be shortened to just
v-if="!$root.currentUserId"

Also v-on:click="return Logout() can be just @click="Logout()" or even @click="Logout"

Well done, enjoy using Meteor + Vue :clap: !

2 Likes