Migrating to FlowRouter, need something similar to a template data context

So I’ve read a lot on the discussion of Iron Router vs FlowRouter.

I started my project using Iron Router, but since changed my mind and I’m currently migrating to FlowRouter.

Everything was going smoothly until I started migrating the comments section of my app. You see, this section is reused several times on the app, it serves as a comment section for news, posts, photos, videos, etc.

Example using IR’s data context:

Router.route('/news/:slug', {
   name: 'newsItem',
   waitOn: function() { Meteor.subscribe('news.single', this.params.slug) },
   data: function() {
      return News.findOne({slug: this.params.slug});
   }
});

<template name="newsItem">
  <p>{{title}}</p>
  <p>{{body}}</p>
  {{> commentSection}}
</template>

The Comment collection schema has a “type” (to know to what type of “thing” this comment belongs to, news, photos, etc). That type was set on the “form .submit” event of commentSection template. Example:

  'submit form': function(e, template) {
    e.preventDefault();
    var $body = $(e.target).find('[name=body]');
    console.log(template.data.type);

    var comment = {
      type: template.data.type,
      parentId: template.data._id,
      parentSlug: template.data.slug,
      body: $body.val()
    };

    Meteor.call('insertComment', comment, function(error, commentId) {
      if (error){
        alert(error.reason);
      } else {
        $body.val('');
      }
    });
  }

This worked because the template data context contained the News item which in turn has a a type property as well.

How could I achieve something similar to this only using Flow Router without setting data on the template as it is recommended by the official guide?

Take a look at the Reusable components in Blaze section of the Meteor Guide, then take a look at the lists-show.* and todos-item.* files in the Guide’s reference app components directory. These files will show you how to properly pass around parent data contexts (i.e. in your case passing the parent newsItem context into the re-usable comments component).

You might find this series of videos useful.

Why are you against the idea of setting that in the template? Just specify a parent template in which you set your subscription and pass the data down from template to template.

Alternatively, go with manuel:viewmodel. This package provides you features called share and mixin. Both you can inject to particular templates in which you want to use them.

In the example above, I use ViewModel.mixin, which is a set of reusable helpers with separate state. In the example below, both listCharactersBox and createCharactersSettingsBox templates have access to game and characters helpers with data.

With ViewModel.share you’ll get helpers with shared state. This way when you change the state of the helper in one template, all other templates that use this helper will automatically see the new value and display it.

characterRoutes.js

    FlowRouter.route('/:game/characters', {
      name: 'listCharacters',
      action: function() {
        return BlazeLayout.render('game', {
          tab: 'listCharacters'
        });
      }
    });

game.html

    <template name="game">
      {{> Template.dynamic template=tab }}
    </template>

game.js

    Template.game.viewmodel({
      onCreated: function() {
        return Meteor.subscribe('game', FlowRouter.getParam('game'));
      }
    });

listCharactersBox.js

    Template.listCharactersBox.viewmodel({
      mixin: ['currentGame', 'gameCharacters']
    });

listCharactersSettingsBox.js

    Template.listCharactersSettingsBox.viewmodel({
      mixin: ['currentGame', 'gameCharacters']
    });

gameViewmodel.js

    ViewModel.mixin({
      currentGame: {
        game: function() {
          return Games.findOne({
            slug: FlowRouter.getParam('game')
          });
        }
      }
    });

charactersViewmodel.js

    ViewModel.mixin({
      gameCharacters: {
        characters: function() {
          return Characters.find({
            "game.slug": FlowRouter.getParam('game')
          });
        }
      }
    });