Help to understand vue-meteor-tracker

I am struggling to understand the Vue meteor option example in the README of the vue-meteor-tracker package https://github.com/meteor-vue/vue-meteor-tracker:

First, I am confused as to whether every Vue meteor option needs all three elements in the example to reactlvely associate the Vue variables with the Meteor collections:
a) the subscription object
b) the function directly searching the Meteor collection, illustrated by the threads () function in the example
c) the object containing two functions, the param function and the update function, the first one seemingly referencing the Vue variable and the second one seemingly using the first one to search the Meteor collection

or whether each of these three properties of the Vue meteor option corresponds to a distinct use case and one will have to pick one of the three in a given circumstance.

Second, I fail to understand what is the direction and order of the data flow between all these variables: which one gets its value first and which one derives its value from which other one and in what order.

Third, the other examples I found on Github do not match this one:

  • the Vue meteor option in the example by @akryum himself in https://github.com/Akryum/meteor-vue-example linked as example project in https://github.com/meteor-vue/vue-meteor-tracker, contains neither a direct function nor an object with a param and a update function but instead a data option nested inside the meteor option, a possibility not mentioned in the ReadMe

  • the TodoMVC example by @mitar found in https://github.com/meteor-vue/todomvc
    also linked as illustrative example in the Meteor-Vue github repository, does not use this Vue meteor option at all; instead, it only contains a Meteor collection subscription wrapped in an autorun call in Vuecreated lifecycle hook option; interestingly, it also does not use the package akryum:vue but nevertheless the akryum:vue-component package (which I did not know was possible) in addition to a vuejs:meteor-integration package (was it authored by akryum as well or by some other Vue.js team member?);
    this approach seems way simpler to understand than the meteor option with param and update functions but perhaps it is far less powerful/complete? Could akryum, mitar or any other meteor-vue integration guru out there shine some light on that?

  • the starlist example by @mwarren2 found in https://github.com/quasarframework/quasar-template-meteor does use the params + update functions in a Vue meteor option, but it puts the Meteor collection subscription inside a Vue created lifecycle hook option instead of a Vue meteor option or a Vue mounted lifecycle hook as recommended in https://github.com/meteor-vue/vue-meteor-tracker: and when I try to do the same, adding a call to this.$subscribe('pubname'), either under a created or mounted Vue lifecycle hook option, I get an error saying: this.$subscribe is not a function

Is there any charitable soul out there using Vue+Meteor in production who would be willing to write a few word to explain all those subtleties to the newcomers to the community?

Shine on me craaaaaaaaaazy diamond!

1 Like

Yes, it is simpler and complete, I would even say more powerful because you can mix both reactivity from Tracker and Vue reactivity (imaging computed value depending on both ReactiveVar and Vue prop).

Downside is that you have to use a fork of Vue. See guide here.

An example of full app using this approach can be found here.

1 Like

Hi Jacques,
Although I did the quasar-template-meteor thing some time ago, I’ve only started using Meteor / Vue / Quasar in earnest quite recently.

In the end I actually have not had much need to use the meteor: option of the akryum code, because you only really need the akryum stuff if you want to use Meteor magic in the ui of a vue component (auto updating of the ui after updates of the db).

So I did actually create a ui that updated positions on a map when someone walked over the map and recorded positions in the db. I used the param and update function and it was stunning! But I realised that actually although it was beautiful I didn’t actually need the auto ui update functionality I’d created, so I removed it to save resources.

Since then I have been happily using perfectly normal Meteor publication and subscription code in my methods to read from the db, and I haven’t needed the ui magic.

To cut a long story short, I’m using Meteor / Vue / Quasar without using the akryum code. You only need that code if you want the Meteor magic in the ui of a .vue component.

@mitar Thank you very much for the lightspeed answer. So in effect your vuejs:meteor-integration package is an alternatve to akryum:vue-meteor-tracker or akryum:vue packages right? But as opposed to the latter, it comes with a full todomvc illustrative example instead of a mere click counter example.

The use case you mention of a Vue computed option with variables depending on both Meteor reactive vars and Vue props could not be done using the akryum:vue-meteor-tracker approach? Why not? What is its inherent limitation that your package overcomes?

@mwarren2 Thank you very much for your fast answer that got me … completely confused now :exploding_head::crazy_face: and hence raises those follow-up questions:

  1. Isn’t MUCH of the appeal of Meteor as opposed to any other JS back-end the pub/sub based automated bi-directional sync between server db and client UI view-model?

  2. How do you make your Vue UI view-model subscribe to the Meteor collection publication without either akryum:vue-meteor-tracker or akryum:vue? Are you using vuejs:meteor-integration?

The way that I see it, using the vue-meteor-tracker package will force you to keep a clear distinction between Meteor and Vue reactivity as data from the former will flow into the latter reactively only via the meteor object discussed above. In my opinion this is a very good thing. If you ever want to move away from Meteor, you’ll have a fairly good understanding of what needs to be updated (anything on the side of Meteor reactivity) and what not (Vue reactivity, which in most cases accounts for more than 3/4 of the app’s code). Also, possibly even more importantly, should you run into some obscure reactivity-related problem in the future, you’ll again be able to likely quickly identify whether the problem is on Meteor’s or Vue’s side.

Regarding your question, the three elements that you mention have separate uses.

  • Element a (the subscription object) deals with triggering the sub and is not strictly necessary to connect Meteor’s and Vue’s reactivity systems (e.g. you could have the data already in the client’s minimongo from some other source or you could be dealing with other Meteor’s reactive source besides collections in which case subs don’t play any role).

  • Element b (function directly searching the Meteor collection, as seen in the ‘threads’ example at github), will let you get data from a collection, but if your query depends on variables from Vue’s reactivity, the query will not rerun if these variables change. As @akryum has mentioned in the comment above the example, in such case, ‘Vue reactive properties will not update’ in the query. You can rely on Meteor’s own reactive source (e.g. Session) in the query though.

  • Element c (the object containing the param and update functions) will enable you make queries to Meteor which depend on Vue’s reactive variables.

I’d say in most cases where you mix Meteor and Vue it makes sense to rely on Vue’s reactivity for most things and use Meteor’s data layer mainly as a tool to inject collection data to parent components. In that case elements a and c above is what you’d be looking at most of the time.

Yes.

I am not sure if you cannot make it work, but it is just nicer that instead of:

meteor: {
  selectedThread: {
    //// Vue Reactivity
    // We declare which params depends on reactive vue properties
    params () {
      // Here you can use Vue reactive properties
      // Don't use Meteor reactive sources!
      return {
        id: this.selectedThreadId
      };
    },
    // Optionally we can watch the parameters for changes in nested
    // objects using the 'deep' option
    deep: true,
    //// Meteor Reactivity
    // This will be refresh each time above params changes from Vue
    // Then it calls Tracker.autorun() to refresh the result
    // each time a Meteor reactive source changes
    update ({id}) {
      // Here you can use Meteor reactive sources
      // like cursors or reactive vars
     // Don't use Vue reactive properties!
      return Threads.findOne(id);
    },
  },
},

You can write:

computed: {
  selectedThread() {
    return Threads.findOne(this.selectedThreadId);
  },
},

Hi everyone! I just published vue-meteor-tracker v2 in beta: https://github.com/meteor-vue/vue-meteor-tracker/releases/tag/v2.0.0-beta.1

Now you can just write:

meteor: {
  selectedThread() {
    return Threads.findOne(this.selectedThreadId)
  }
}
5 Likes

Awesome! Will try it ASAP!

And can Meteor Methods be called in the body of functions defined in the Vue Methods option?

Take a look at this example: https://github.com/meteor-vue/vue-meteor-demo/blob/master/src/imports/ui/Notes.vue

@akryum, considering the chronic lack of positive feedback plaguing most open source projects, let me just say thank you very much for your work on the Vue-Meteor integration. At this point I’m pretty heavily invested in Vue working nicely with Meteor and so seeing these packages receive some attention is a very positive sign for me.

And while we’re at it, props to @mitar as well for your Vue related work. Much appreciated.

5 Likes

It just got simpler: https://twitter.com/Akryum/status/995859369324576771

export default {
  data () {
    return {
      limit: 5,
      sort: true,
    }
  },

  created () {
    // Not SSR friendly (for now)
    this.$subscribe('notes', () => [this.limit])
  },

  computed: {
    notes () {
      // Not SSR friendly (for now)
      return this.$autorun(() => Notes.find({}, {
        sort: { created: this.sort ? -1 : 1 },
      }))
    },

    firstNote () {
      return this.notes.length && this.notes[0]
    },
  },
}
3 Likes

Well, first much thanks :+1: to @vooteles and @mitar for your messages that went a long way towards clarify things for me concerning vue-meteor-tracker and then super-duper-fly-thanks :clap: to @akryum for making this fresh understanding …already obsolete with his new leaner and meaner beta version which looks full of possibilities!

Very encouraging for the future of the Meteor+Vue stack!

Is this.$autorun() method only available in the meteor or computed option or can it also be used in the data option?

It’s available in computed properties.

Hi Jacques,

I’d also like to thank @akryum especially after the latest code changes, which basically bring the syntax in line with Meteor syntax. Great work.

To answer your first question: a lot of the appeal of Meteor is the bidirectional sync, yes, but I think that has rather obscured a lot of common-sense ways that they have made things simple.

  1. I like the pub/sub system and the way it combines with the accounts system. You get security and authorization out of the box.
  2. Then there’s the build system which just works without configuration. Compare that with webpack.
  3. es6 just works as well.
  4. The bidirectional stuff is wonderful, but before using it on a big system, you have to make sure that you’re using it carefully and don’t run out of resources. It’s not the only reason I use Meteor.
  5. There’s an ace team behind Meteor, that works constantly to keep things simple and ensures that when they write new stuff, existing code just keeps working.

In many cases you can just use Meteor code normally. For example on the home page I have a select object for choosing a name, which I fill from a Meteor.call() (apologies for not converting to arrow function syntax)

methods: 
{
            //we don't need reactivity, so we use a Meteor method
            getWalkNamesArray: function () {

                let self = this;
                Meteor.call('getWalkNames', function(error, result){
                    if(error){
                        console.log('error', error);
                    }
                    //quasar lists (selects) expect an object with label and value
                    result.forEach(function (doc) {
                        self.walkNames.push({label: doc.walkName, value: doc._id});
                    });
                });
            }
}

The function is called from here:

        created() {
            this.getWalkNamesArray();
        }

and the select object in my .vue component uses quasar-framework code (which uses vuejs reactivity) like this:

            <q-select
                    type="list"
                    v-model="id"
                    float-label="Walk name"
                    color="secondary"
                    inverted
                    style="width: 50%"
                    :options="walkNames">
            </q-select>

I have other code where I use pub/sub instead:

       watch: {
            //watch for _id (or the subscription) to change - sent from parent Home.vue which has the walk names select object
            _id: function(){

                this.walkSubscription = Meteor.subscribe('walkLocations', this._id);
 
                Tracker.autorun(() => {
                    if (this.walkSubscription.ready()) {

                        //if the place for the map is in the DOM
                        if($('#' + this.mapId).length > 0){
                            this.setUpMap();
                        }
                        this.prepareMarkers();
                    }
                 });
             }
        }

Then in prepareMarkers() I have an ordinary call to the subscription:

               let currentWalkLocations = WalkLocations.find(
                    {
                        walkId: this._id
                    }
                );

So, to sum up: I started off thinking that I was going to need a lot of third-party code to use Meteor with vuejs, but they work together anyway for some of the usual scenarios, also because vuejs has its own reactivity.
It’s worth trying just Meteor / Vuejs first, then adding third party stuff if necessary afterwards.

@mwarren2 thank you so much for this detailed answer :+1::+1:, but let me check that I understood it.

Your getWalkNames Method accesses a list stored on the server that happens to be unmutable which is why you don’t need Meteor client-server reactivity to access it from the client. Is that right?

You are saying that it is possible to call Tracker.autorun and Meteor.subscribe in a Vue component option to get client ViewModel - server Model reactivity by just inserting an import 'meteor/meteor' declaration into the Vue component instead of using the special variables $subscribe and $autorun of the vue-meteor-tracker module?

And that since Vue gives you View-ViewModel reactivity out-of-the-box, in most cases that is all what is needed to reactively percolate changes (triggered by a user acton on a client 1 Vue template View intercepted by a v-on directive):

  • from clent 1 Vue ViewModel to client 1 Vue template View through Vue reactivity,

  • then to a server Mongo record shared through Meteor pub/sub with a client 2,

  • then to the client 2 Vue ViewModel by Meteor reactivty,

  • then to that client 2 Vue template View via Vue reactivity

  • and then back in the other direction whenever the user interacting with client 2 also triggers a change.

If this is indeed what you meant, then what is it exactly that one can achieve easily with $subscribe and $autorun and/or the meteor Vue custom option of the vue-meteor-tracker module that one cannot achieve as easily with mere Meteor.subscribe and Tracker.autorun calls in Vue code into which one just imported Meteor? :thinking:

Jacques, in reality I’m not that far ahead of you in all this, so bear that in mind.

What I have recently discovered, as I have attempted to show you with some code, is that to a certain extent Meteor and vuejs work together without any help. Bear that in mind as you start coding is what I’m saying.

I can’t give you great specifics because I’m honestly only starting to code in earnest myself right now.
I did work on quasar-framework / vuejs to get quasar working with meteor because I wanted a nice ui and it looks great and all the others gave me problems.

Sorry if that’s not enough but my own experience is still too limited to give you much more.

However normal meteor pub / sub works in vuejs and so you can fill an array saved on the context (this.someKindOfArray) and then rely on vue to react and show someKindOfArray in the ui.
getWalkNames is not totally immutable - at some stage a walk might be added, but there is no benefit in making it reactive, it can just wait for the next page refresh.

Yes, it’s possible. But I presume you will need akryum code if you want auto updating in the ui. I say presume because I haven’t got around to trying all the possibilities yet. I’ve got quite a bit done without actually using the akryum code.

I wish I could help more, but I’m still working it all out as well.

@mwarren2 Thanks very much!

so @akryum, @mitar, @gusto, @vooteles, @trimurtix, @herteby and other Vue-Meteor ninjas out there: do you confirm that full bi-directional reactivity between:

  • Vue Templates
  • Vue Instances
  • Meteor client side Minimongo Collection cache
  • Meteor server side persistant Collections

just works out-of-the-box with bare Meteor + bare Vue and that the akryum:vue-meteor-tracker or vuejs:meteor-integration packages are only needed to bring full reactivity with hybrid Vue+Blaze UI clients?

And since I am at it with my nagging clueless noob questions: what can one do more easily with a hybrid Vue+Blaze UI than with a pure Vue UI? I always thought that Vue was a replacement for Blaze not a complement to it, so I never quite understood the motivation behind all the example repos on Github with Meteor clients containing both Vue and Blaze code :thinking:

See below an answer to a related question that @akryum posted on the Vue forum:

The purpose of vue-meteor-tracker is to make it easy for you to use Meteor reactive data inside Vue components, without having to care about handling every thing yourself (like teardown when the component is destroyed, or auto-update if you depend on a Vue reactive prop in your Meteor data).