Patterns and practices for passing data between templates

I’m curious of this is applicable? What is the beneficial for this package and couldn’t understand the code examples in the readme.

1 Like

Hi, I am the creator of that package.

I did it because I am trying to figure out the best way to use Flux and Meteor together.

There was already an implementation of Flux & Meteor called space-ui. It’s great but for me it tries to do too much stuff. Facebook implementation (and my port) only has a dispatcher because in the end, the dispatcher is all you need. I am in the “the less externals packages you need the better” as well.


Back to the topic, I think you should look for a pattern for your whole app, not only one to pass data between templates.

MVC is great and you can do it with IrounRouter, for example. But that feels like coding a website instead of an app. Like using PHP or Ruby instead of Meteor. The user changes the URL, the controller asks for the data and renders a new view. End.

What you want to do is more like how an app behaves. The user clicks something and other part of the view changes. No url changes. No whole page rendered.
Meteor is great for this, we all know that. But if you don’t use a pattern, you are asking yourself all the time “Where should I put this logic?”, “How do these components communicate?” and things can get messy in a short time.

This is how passing data between templates works in a Flux application:

1- Views are only allowed to dispatch actions. When the user selects a post, this happens:

Template.PostView.events({
  'click .post': function(){
    Dispatcher.dispatch({
      actionType: "USER_HAS_SELECTED_A_POST",
      id: $(event.target).attr('data-id')
  });
});

2- The Stores are in charge of the App State. They listen to actions and modify the App State in consequence:

// Create the Store. Use the way you prefer. Objects, functions, prototypes, 
// classes... it doesn't matter.
PostStore = {};

// Save the App State in local reactive variables.
// If you want it to survive to Hot Code Push use ReactiveDict instead of
// ReactiveVar. Both are MDG packages.
var selectedPostId = new ReactiveVar(false);

// Register to actions and map them to callbacks.
// This "switch" thing looks kind of dirty but it's very flexible.
// It's the way Facebook people do it.
Dispatcher.register(function (payload) {
    switch (payload.actionType) {
      case "USER_HAS_SELECTED_A_POST":
        PostStore.on.postSelected(payload.id);
        break;
    }
  });

// In the Store, you have callback methods.
// They modify the App State when something happens.
// Collections, reactive variables, private variables... whatever you need.
PostStore.onPostSelected = function (id) {
  selectedPostId.set(id);
};

// Finally, create methods to retrieve content.
// Views or other interested Stores can use them.
PostStore.getSelectedPost = function() {
  return Posts.findOne(selectedPostId.get());
};

3- Back in the View, you just retrieve that value where you need it:

Template.DetailView.helpers({
  selectedPost: function () {
    return PostStore.getSelectedPost();
  }
});

Dispatching actions decouples your app. Now, if you want to do something else when the user selects a post, you don’t need to touch the View. Just register your new Store for that action.

I have created the whole example and uploaded it to github and meteorpad:

http://meteorpad.com/pad/36dwXz9ktQK3SJGgB

I know this is very simple but I am trying to create a complex example to see what problems you can run into. So far so good, but it’s still a work in progress.

This is the thread:

Any feedback or help is welcomed :smile:

4 Likes

By the way, the package @proyb2 mentioned, reactive-dependency, can be used to get rid of globals, use dependency injection or circular dependencies (one object needs another and vice versa).

This is another example of Flux using this package:


As you can see, there aren’t any globals and load order is not a problem.

Stores are just javascript objects, so you don’t need to use Flux to use this package.

1 Like

Check some of my ideas about this topic in Blaze Components documentation: https://github.com/peerlibrary/meteor-blaze-components#accessing-data-context

Personally, I think there are three ways to pass data around: as data context (when you want to keep the component persisted, to keep some internal change), as component arguments (when you want for it to create), or by traversing components up and down the tree.

All of those are something what you can already do in Blaze. But it is not so clean API to do it.

For me, the most useful thing before making Blaze Components was this helper:

With this you can access fields on parent template instances. And then it is really easy to pass data around with explicit API. So in the parent template instance (or component) you provide methods you want, for an API you want, and then children template instances (or components) can talk to that. I much more like such explicit APIs.

And those methods can even be reactive.

2 Likes

While I have no knowledge on this pattern, I think Reactive Presentation Model could be an interesting additional feature for Meteor by providing an extra layer that wrapped data between logic/model and view.

RPM is just a random term I came up.

@Ben, for now, blaze-component and flow-component don’t tackle component communication issue. Its looks like @mitar is actively evaluating and working on a solution for blaze-component. @manuel s Viewmodel package has an excellent way of handling component communicate (http://viewmodel.meteor.com/#compCommunicationBk) and I think its a good start for other component packages to follow.

I think the key to component communication is

  1. Api to access instance of template/component
  2. Api to access instance states for side-effects
2 Likes

I was reading over this post again, and noticed this:

Reading the docs I’m still confused: http://docs.meteor.com/#/full/meteor_subscribe

This excerpt from the docs leads me to think we should have the subscriptions inside the autorun:

If you call Meteor.subscribe within a reactive computation, for example using Tracker.autorun, the subscription will automatically be cancelled when the computation is invalidated or stopped; it’s not necessary to call stop on subscriptions made from inside autorun. However, if the next iteration of your run function subscribes to the same record set (same name and parameters), Meteor is smart enough to skip a wasteful unsubscribe/resubscribe. For example:

Tracker.autorun(function () {
  Meteor.subscribe("chat", {room: Session.get("current-room")});
  Meteor.subscribe("privateMessages");
});

This subscribes you to the chat messages in the current room and to your private messages. When you change rooms by calling Session.set(“current-room”,
“new-room”), Meteor will subscribe to the new room’s chat messages, unsubscribe from the original room’s chat messages, and continue to stay subscribed to your private messages.

If more than one subscription sends conflicting values for a field (same collection name, document ID, and field name), then the value on the client will be one of the published values, chosen arbitrarily.

I have started using @mitar’s Blaze Components and it has solved my data-passing woes between templates. Honestly, it almost feels like this is how Blaze should’ve functioned from the beginning even though it is a little bit more complicated and somewhat turns so many tables on Blaze (in a good way). this inside your events && (methods || helpers) is not the data context but the template instance and the data context is either this.data() or this.currentData(). So it essentially does away with having to call the awfully verbose Template.instance().

The fact that I can simply call this.reallyFarOffMethodUpTheChainInAParentParentParentTemplate() without having to hot-potato my data down the templates and hope that it’s there is extremely helpful.

Also, no you don’t need to wrap a subscription that takes no parameters inside an autorun and Meteor knowing not to waste an unsubscribe/subscribe is not contingent on it. Wrap them only if they take reactive data sources (Session, ReactiveVar, etc) as parameters.

4 Likes

@aadams Thanks to post amazing topic in advance,
I totally agree all of your opinions, but it seems there’s no perfect solution for now, unfortunately.

Have been using Meteor for an year, I’ve been curious why there’s no perfect data deliver component to carry all fluently.
I think this curious came from mobile programming such as iOS, Android, and they all have their Navigation systems and their own data context delivery containers.
Each screen might have a data context and it could be delivered, and at the first time, Iron Router seems like solving this problem.
But for some reasons, I was tired to use this.

By the way, the way I found is using Blaze.view object.
i.e. in the Template.onCreated methods,
I can access a single ReactiveVar, top of blaze template, something like this.
Blaze.getView(‘with’).dataVar.get() (and even set() available)

I know someone suggested using Blaze to solve this above,
and this would be dangerous when Meteor updates Blaze core in the future,
I thinks this is the only way to inject data when the subscription.ready() in onCreated method for now.

I think someone who wants to bind and prepare all data in one place will understand why we should use this pattern.

I guess now’s a good time to mention React.js’s top-down approach to passing data.

Instead of child templates reaching above for data, it’s the parent’s responsibility to pass things down and the children must work off of that. Not only do they get their data from the top, they also get actions too that they invoke while possibly passing data to it, which is interesting.

The only immediate down-side is that you’d think you would have to pass down things a huge chain, but getChildContext helps solve that problem.

In addition, there’s Flux, Redux and whatever other Ux I may have missed that act like a giant mother-ship of data and actions as a means for interacting between components. Correct me if I’m wrong on these.

No way to use as below?

{{> myTemplate myParams}}

That is too bad. If I want to input param 0 to display red and param 1 to display blue in two use cases, it is not convenient. This Blaze template is too restrict.

Thanks @lai,

This whole thread is related to Blaze template intercommunication.

And honestly from what I’ve read and watched, I can’t believe there’s so much hype around React. I think even Angular 2 seems better from what I’ve read.

@jcarson, I agree, I’m looking forward to Blaze 2.0 hoping for improvements in this area.

I have a pull request in to Meteor to add a parentTemplate call that will allow you to find a template and then you can get at vars you’ve stored for the template. You can view it here: https://github.com/meteor/meteor/pull/4449

2 Likes

Thanks @joshowens, by the way your podcasts are great – your contributions to the community are invaluable. And thanks for the pull request!

The data I am passing is between sibling templates – so one template was not embedded within another. It is a situation where I had a list of users, and when I click on the user, I get taken to another template (a details page for example) where the Id (and other stuff) was passed to it.

In this situation, the best way I found was to just bite the bullet and use a Session variable. Of course naming is important (as you’ve spoke about before on your podcast several times).

While I agree with your particular case, in my case I was able to use a reactiveVar and I was creating a package that had a child template you would include. The reactiveVar is on the parent. There is no way to access a parentTemplate instance itself, just parentData… Which isn’t very helpful because it isn’t super easy to set a data context programmatically.

{{> filterInput}} reads much cleaner than {{> filterInput filter=reactiveVar}} imo.

for the love of god just use React if you need to manage states

4 Likes
  1. Why autorun in the following?
    Isn’t it already reactive?

Template.posts.onCreated(function () {
var self = this;
self.autorun(function () {
self.subscribe(‘posts’);
});

  1. I am presently investigating Blaze Components (maybe it can help communicate data between templates)

We’ve started using viewmodels to manage UI state

2 Likes

Why did you go this route?