Patterns and practices for passing data between templates

I recently read this article by @sashko about data flow from the database to the UI – it was fantastic and really help synthesize the concepts for me.

But I for one still have confusion around patterns for the flow of data between templates.


As of Meteor 1.1*, what are the best patterns and practices for passing data between templates using Meteor?

6 Likes

Do you specifically mean passing data between parent template to a child template, between random templates, or do you really just mean patterns for passing data to templates in general.

I myself am curious about the last, but I wonder if it wouldn’t be a good start just to list all or a bunch of the ways to actually do it, and then start discussing the merits of one versus another.

1 Like

Not sure what “raw” Meteor is. Data in what form? Literal MongoDB data? Or just data as in variables? You can use Session.set() and Session.get() to pass things between templates. Or client-only Mongo collections, e.g.:

MyThings = new Mongo.Collection(null);

Yes, I’d recommend either Session.set() / Session.get() or use client/local collections as I mentioned above.

Thanks for your post.

In what form…

// client.js

Template.posts.onCreated(function () { 
  var self = this;
  self.autorun(function () {
    self.subscribe('posts');
  });
});
Template.posts.helpers({
  user_messages: function () {
      return posts.find({ }, { sort: { posts: 1 } });
    }
  }
});

// server.js

Meteor.publish('posts', function () {
    return user_messages.find({});
});

The above is how I currently subscribe to a server publish for data about User Posts (stripped down of course).

This data would be displayed in a list.

{{#each messages}}
  <tr>
    <td>{{user_id}}</td>
    <td>{{posts}}</td>
    <td>{{view_count}}</td>
    <td>{{last_updated}}</td>
  </tr>
{{/each}}

If I wanted to send the userId to a Post Details template, when the user selects an item from the list – what would be the best pattern to do something like this, for example?

By raw Meteor, I mean not using something like an Iron Router ‘:_id’ and a URL parameter. And not another package that abstracts away any complexity.

But also, I’d think we’d want to shy away from using global reactive variables, i.e., Session. A pattern that doesn’t use JQuery.

Isn’t there another, better pattern? One that does things the ‘Meteor’ way?

The “Meteor” way would be to use Iron Router. Why avoid it? And why would we want to shy away from using Session? It’s a perfectly viable solution to global reactive vars.

I suppose the question I have is: these things within Meteor are meant to make your life easier. Why eschew them? And yes, if you wanted to send the user_id to a post details template, I would use Iron Router and the argument :userId. Though wouldn’t you typically pass the _id of the post itself to a post details template?

1 Like

I’m sure there are several different patterns for solving this problem (there always is). What are they? What are the advantages/disadvantage to each?

For example I’ve heard, use: ReativeVars, Sessions, Parameterized URL (with IR’s ‘:_id’), Blaze.renderWithData()… I’m sure there’s more.

With the way I’m setting up my template with data, is there a clear winner?

What do the Meteor thought leaders say on this topic?

Thanks.

As I gain experience with Meteor, I’ve learned one thing for sure, the less dependencies on these ‘helper’ packages the better.

Meteor seems to be evolving quickly, and I’ve seen plenty of cases where packages don’t keep up or just fall off (I’m sure you can name at least one). I’d rather retain as much control as I can… but I understand they have their place.

For something as basic as passing data around template-to-template, I’d rather this core functionality stay more under my direction (even if I have to work harder).

1 Like

You are right, Session is a global variable and should probably be avoided for big projects.
However, it is very useful…
One replacement possibility that is not global is the reactive-dict.
It has all the advantages of Session (reactive, easy, persist between hot codes push…) but you can create as many as you want and keep them in restricted scopes.
However, one moment or another you have to use a central (= global) way for your templates to communicate.
One way to do it is to use a “registry”, one global singleton object which is used to communicate shared objects (example : a reactive dict !) between the parts of your app that need it.

I’m curious what’s wrong with using Session.

It’s a global---------------

Collections are global too. If I define this:

Posts = new Mongo.Collection('posts');

Then Posts is accessible everywhere. It’s global.

Yes, collections are usually globals. The important part is “usually”. You can limit their scope if you want to, you can have a Posts variable refering to a collection in a part of your app, and refering to another collection (of whatever you want) in another part.
The problem with Session is that you cannot do that. Every part of your app has access and can modify every Session variables, even when that’s not intended.
Limiting the use of global variables is a base rule of programming.

I don’t think this is too valuable for this discussion, but to explain:

I’m not actually building a Forum system, I just used it as example to make my problem easier to explain.

I’m actually building a messaging/communications system.

The Posts screen I referred to will just be a list of all the users in the system. When you select that person from the list, you’d go to a Posts Detail screen, which would actually be all the Posts, or in my case Messages, the User has ever sent back-n-forth with the Client.

Or put another way, in my case the Posts are not topic based, but User based.

@aadams I get where you are coming from. I have a post similar to yours here Inter component/template communication (architecture)

The reason this question keeps coming up is that templates and components solve most pain point but fall short with in the communication department. Can you imagine html controls (buttons, inputs etc) communicate through session variables? I cant. Communication between html controls is achieved through control properties and events, making each control standalone at the same time, providing a decoupled communication channel. You can set properties, subscribe or unsubscribe to events to cause or react to state changes. Polymer stays with this concepts and its much easy to do inter component/template instance communication.

I also think a centralized communication broker (emitters, etc) is a good options if its not a parent-child relation since it takes decoupling a step further (controls don’t reference each other). If its a parent-child relation, you can use referenced properties. Exploring and building a good central broker is needed by the meteor community, I think. There are a few like https://atmospherejs.com/raix/eventemitter (using it for my current broker implementation). Pure event brokers have their limitations. React proposes a similar mechanism for non parent-child component communication - http://facebook.github.io/react/tips/communicate-between-components.html

@mitar release a great blaze-component - Announcing Blaze Components - Reusable components for Blaze and I plan on expose blaze-component as web-components so we can communicate between components using the good old control properties and events, just like html. We could also do the same for blaze.

2 Likes

My previous post was more of “I feel your pain” and less of here is an/the answer. From my part, this is my rule of thumb

If its between pages/routes, use router variables/parameters
if its between a parent and child template, use property reference, namespaced session variable or local vars
if its between a non parent-child template, use centralized broker system

2 Likes

In meteor world (or in any reactive javascript client app world for that matter) pages, parent/child templates and separate templates are in fact in the same top level reactive context so why provide separate solutions?

One global reactive solution (probably a message/event broker/bus) would work for all the cases and keep everything simple.

In fact, here’s a video that describes exactly how I feel a solution should bring on the table for this kind of problem

2 Likes

@serkandurusoy I’m not a big subscriber to the one hammer for every job. If you are building a simple app, you might not need tools built for complex apps but I see your point. I think every development/case is different and knowing your options and where they make sense can help people make decisions.

3 Likes

This seems like sound advice to me.

To you, what is the difference between:

and

To me, this can be one and the same… In fact, the two Templates I need to pass data between (‘Posts’ -> ‘Post Details’) are just Templates with their own Route (in Iron Router), their own Subscriptions (two different Collections even), Helpers, and Events. The only reason they are ‘linked’ is because one page has a [userId] and the other page should take this [userId] and do a filter on a Collection with it.

I mean, what I’m really doing is a [join] between two Collections… I want to pass the [userId] to a Publish method on the Server so I don’t have to pull all Posts for all Users to the Client and do my filtering there.

Right now I’m using [onCreated] (the newest version) to do a Template level Subscription (instead of [onWait] in IR):

Template.user_messages_detail.onCreated(function () { 
  var self = this;
  self.autorun(function () {
    self.subscribe('user_messages_detail');
  });
});

And the corresponding Publish:

Meteor.publish('user_messages_detail', function () {
    return user_messages_detail.find({});
});

I’d like to pass as little as possible to the Client and just do sorting and security trimming there. So modifying the code above, what I’d ideally like to do is pass a [userId] to the [onCreated] function:

Template.user_messages_detail.onCreated(function (_USER_ID_) { 
  var self = this;
  self.autorun(function (_USER_ID_) {
    self.subscribe('user_messages_detail', _USER_ID_);
  });
});

and this this would pass into the corresponding Publish:

Meteor.publish('user_messages_detail', function (_USER_ID_) {
    return user_messages_detail.find({_userId: _USER_ID_});
});

This would solve my problem (I think).

But how to get [USER_ID] from one Template to the next Template?


This is the ‘official’ Meteor forum, just think how valuable it would be for someone from the MDG to add commentary, provide guidance and direction on this important topic?

2 Likes

I’m of the same opinion.

Different tools (patterns) for different problems I always say… in fact as you pointed out earlier, different tools for the same problem too.

Embrace the situational.

Don’t subscribe to one ideology too harshly.

Avoid dogmatism — specially when it comes to software development.