Best place to fetch documents: iron:router or template helpers?


#1

Hi all,

I’m new to Meteor and I’m currently building a web app. I’m wondering what’s the best place to fetch documents between everything in the route definition using the data property, or everything in the template helper or a mix of them.

Currently, I’m mixing it. For a page with a collection, I get them from the template helper. But on a details page, since I get the document id from the route, I fetch the document within the route definition.

But, as an enterprise developer, who’s used to layered architectures and design patterns, it feels strange to access data on different places.

I’ll read your input with great interest!

Regards,
Fabian


#2

Currently I’m doing things the same way as you do.
I’d also love to hear some answers to this.


#3

I would advise against using Iron Router data function, especially if you are new to Meteor.

data is called within a hidden autorun, meaning that any single reactive change within data will trigger a new call to data and a full Blaze update sequence.

IMO, the right way to fetch data is:

  • to use a template helper directly if data is used once,
  • to use a a template helper and a local #with if data is used more than once within a template,
  • to use atomic reactive storage (Session, reactiveDict, reactiveVar) in other cases.

#4

Thanks for your input Steve!

So, if I want to move document access to tempate helpers on details views, should I store the document id in the data function of the route or within the Session?

Thanks again,
Fabian


#5

The data function. This way, you can just call template.instance().documentId inside your template without having to care where that id is actually coming from. And if you ever decide to switch to a different router later you won’t have to change your template code.

If you’re only using data for the _id, the problems @Steve was talking about don’t really apply.


#6

Thanks for your answer Sacha. I’ll change my application accordingly. :wink:


#7

Would you advise me to return only the id or and object with an id property set to the id value?

data: function () {
    return this.params._id;
  }

VS.

data: function () {
    return {
      id: this.params._id
    };
  },

#8

I would go a little further and return an object. Templates can then also be sensitive to the context in which they are being used.

data: function() {
  return {id: this.params._id, somethingAboutThePath: true};
}

#9

Thanks robsw, I also thought it was the right decision. My only concern now is that my whole template content is surrounded with a {{#with}} which looks strange to me.

Template.theTemplate.helpers({
    myObject: function() {
        return TheCollections.findOne({_id: this.id});
    }
});

<template name="theTemplate">
{{#with myObject}}
..
{{/with}}
</template>

#10

Yes I find that a bit cluttered too. Sub templates can help

{{> subTheTemplate obj=myObject}}

<template name="subTheTemplate">
   ...{{obj.field}}...
</template>

Forgive the syntax but you can see the pattern :smile:


#11

Yes, I see it. But, it’s still not looking good (to me) to have a template only to display a sub template :wink: But, I guess I’ll go this way for now. It will be always time to refactor with a better idea later on.

Thanks all of you, help has been really appreciated!
Fabian


#12

Hmm, now that I know about the iron:router “dataNotFound” plugin, I’m asking again the question. Since the plugin renders the not found template if data is falsey, I’m not anymore sure that the solution we came to is right.

Docs: https://github.com/iron-meteor/iron-router/blob/devel/Guide.md#plugins


#13

Just some food for thought… I’ve been struggling to understand the best pattern for this use case as well… that’s a great suggestion to use the data feature of Iron Router to pass along the ID of the document you plan to fetch in the template helper!.. I never thought of that.

I just want to add that I have been playing with both iron router and flow router and flow router provides a way to get the route parameters easily in the template helper:

Template.chart.helpers({
    chartDocument: function () {
    var id = FlowRouter.getParam('_id');
    detailChartsSubsManager.subscribe('chartdetails', id);
    var cd = Chart.findOne({_id: id});
    return cd;
  }
});

in fact, Flow Router warns people against trying to get route params in the helpers, and says that its FlowRouter.getParam() function is the way to go.

But it seems that passing the ID along in the data parameter using Iron router is pretty much the same thing as getting it with .getParam using FlowRouter…

My biggest issue right now is understanding where to put the call to .subscribe… sometimes I see it in the route, and the .find is in the helper, but then there’s the case of doing all the waiting, and checking for whether the routes are ready before rendering the template, etc, etc…


#14

wesyah234, here a sample route from my project, you can see how I use the subscribe in the waitOn.

Router.route("/fournisseurs", {
  action: function () {
    this.render("supplierList");
  },
  name: "supplier.list",
  waitOn: function () {
    return Meteor.subscribe("suppliers");
  }
});

#15

Thanks!! @fvilers! that will help


#16
  1. Use Flow Router
  2. If, for a reason, you need to use Iron Router, don’t use data(). Use this instead:
Template.chart.helpers({
    chartDocument: function () {
    var id = isolateValue(function () { return Router.current().params._id; });
    ...
  }
});

You need isolateValue to limit reactivity. There are other packages, potentially better, to achieve the same.


#17

What if my data function returns more than one post or any other collection.I mean are we bound to use findOne while using data function?