Trying to subscribe in onCreated, create ReactiveDict


#1

I’m a little confused. Trying to understand better the differences between ReactiveDict, Var, and Session, I’m trying to assign a Collection to a ReactiveDict, but I can’t seem to do so. I’m new to this.

What am I doing wrong here?

Template.body.onCreated(function(){
    Meteor.subscribe('itemFind');
    this.global = new ReactiveDict();
    this.global.set('items',Items.find());
});

Template.body.helpers({
  items(){
  console.log(Template.instance().global.get('items'));
  return Template.instance().global.get('items');
}
});

?

I’ve gone through a bunch of steps here with no luck.

http://stackoverflow.com/questions/40597947/meteor-reactivedict-mongodb-find-oncreate/40599128


#2

Hi

Try this:

return Template.instance().global.get(‘items’).fetch()


#3

No, that doesn’t work.

Template.body.onCreated(function(){
  // Subscribe to the item
  Meteor.subscribe('allItems');
  //setup the dictionary
  this.global = new ReactiveDict();
  // set the dictionary for items
  this.global.set('items',Items.find().fetch());
});

It returns an empty array.


#4

It returns an empty array.

So maybe you have no items available on the client side. Check your subscriptions.


#5

The collection comes back in a helper, it just doesn’t come back in the onCreated, or the onRendered for that matter. The subscription is correct, it just seems that the helpers are running before the onCreated or the onRendered (if I had to guess).


#6

Not exactly sure why you’d set the collection as a property of a ReactiveDict, but if you wanted to, the following should work. Also, it’s not really best practice to assign properties to the body template.

Template.body.onCreated(function(){
  this.global = new ReactiveDict();
  this.autorun(function() {
    let itemSub = this.subscribe('itemFind');
    if (itemSub.ready() {
      this.global.set('items', Items.find());
    }
  });
});

Template.body.helpers({
  items() {
    console.log(Template.instance().global.get('items'));
    return Template.instance().global.get('items');
  }
});

#7

Well, I guess I’m trying to understand why I’d want to query that collection more than once in the individual helper methods, which has lead me to believe a single collection object query would be the smartest thing to do. Am I wrong?

Also, I’m trying to understand how the ReactiveDict, ReactiveVar, and Session work throughout Meteor, as I’m new to this platform.


#8

Once you’re subscribed to the data, you can do something pretty simple in your helpers, like:

Template.myTemplate.helpers({
  items () {
    return Items.find();
  }
});

In general, I’d say stay away from Session as much as possible. It’s easy to get hooked on Session, as it’s available everywhere in the client side, but makes problems quickly with its global scope. Also, you can scope RD and RV to the template, which makes for easy cleanup.


#9

Ok, I totally understand the Session, RD, RV parts.

Would you suggest, after subscribed, that Items.find() should be called individually in each helper method? Is that best practice? Doesn’t that double the items? So don’t they need to be cleaned up? Or checked for existence?

I’m a little confused about that part. How do you not double the documents selected?


#10

I guess I don’t understand the use case?

In most cases, you would have something like the following in your template:

{{#each items}}
  <p>{{name}} - {{description}}</p>
  <p>${{price}}</p>
{{/each}}

So then you can pass your cursor (or an array, or a falsey value) and the #each will handle it in your template.


#11

Leaving aside the use case, which I’m also struggling with, the reason the collection “doesn’t come back” in the onCreated or onRendered is that these are not reactive. If what you want isn’t already available when they run, it won’t ever be available there, unless you make them reactive. On the other hand, helpers are reactive and will re-run as necessary to accommodate changes in (for example) collection data - as it arrives, when it’s added, changed or deleted.


#12

Apart from the fact that I do not understand WHY you want to do this, try

Template.body.onCreated(function(){
  var self = this;
  //setup the dictionary
  self.global = new ReactiveDict();
  // Subscribe to the item
  Meteor.subscribe('allItems', function(){
   // wait for the subscription to be ready and set the dictionary for items
   self.global.set('items',Items.find().fetch());
  });
});

#13

Ok, I get the impression I’ve gone off the rails on this train of thought. My understanding was simply that numerous .find statements would replicate the collection. I’m guessing I’m wrong about this?


#14

Indeed wrong. Subscribe copies whatever the server publishes to the client, and find on the client runs against the local copy. And collections are reactive already, so no point in copying the result of find into a ReactiveDict.


#15

Ok, so here’s probably where I made a mistake:

If I have two helpers, both doing a Items.find();, they’re both hitting Mongo, let us say, is the overhead not something to be concerned about? Or rather, is there a better way to OOP a .find() that’s going to be replicated/put the results in that .find() into a RD?


#16

They are hitting the minimongo on the client. That is the whole pub/sub of Meteor. Read the guide


#17

Ok, so I think my learning lesson here is that after subscription just access them in the helper method.


#18

Just wanted to share something as you use Blaze as the front-end

To avoid numerous .find in helpers, you can do something called registerHelpers

Template.registerHelper('getMyCollection', function (){ return myCollection.find() });

And then use it in *.html files -> {{#each getMyCollection}} <p> {{name}} </p> {{/each}}

This will scope the helper to every template you can create. And so, avoiding many repeating :slight_smile:
Hope it will help


#19

This is exactly what I was looking for.

Awesome. Thanks.


#20

As a little postscript here…I doubt that this was exactly what you were looking for :wink: [quote=“rolljee, post:18, topic:31382”]
Template.registerHelper(‘getMyCollection’, function (){
return myCollection.find()
});
[/quote]
All this does is create a global helper to do what you were worrying about doing as a specific template helper! You were worrying for, I’m pretty sure, performance reasons and this solution does not make any efficiencies in that regard.

Global helpers are not namespaced (unsurprisingly) so, again, you want to use them sparingly. The answer to your question is more along the lines of don’t worry, performing a find() each time in helpers is fine as they operate on the local minimongo and, being client-side, would have limited extent. And, of course, the find() is reactive.

If, for some strange reason, you needed to do lots of ````finds()(hundreds) on a large client-side collection AND you did not need reactivity then you'd store the result of afind()``` in a locally scoped array variable and use that:

var myresults=collection.find().fetch();

Even then, I’d think the performance savings would be marginal.

So, just use find() in your helpers and stay cool.