Where to subscribe to a publication so FlowRouter can access it?


#1

My app is using React, but I’m not sure that’s relevant in this case.

What I’m trying to do is use a Mongo collection that stores slugs associated with names of React components. Flow Router should be able to access this collection. The problem is, I’m not clear on where to put my Meteor.subscribe so that this code below will work properly.

FlowRouter.route('/:slug', {
  action(params) {
    let tmpl = Templates.findOne({slug: params.slug});

    if (tmpl) {
      ReactLayout.render(eval(tmpl.component));
    } else {
      ReactLayout.render(NotFound);
    }
  }
});

#2

Flow Router is built to route, not to handle anything subscription-related. For Blaze-users, this means handling subscriptions in templates. I wouldn’t know how to handle it for React.


#3

This problem would also exist in Blaze. Before any templates have even been rendered, I need to pull data from a collection and use that information when deciding how to route a user.


#4

1 render a loading page
2 wait for subs to be ready
3 figure out if they user should be routed to A or B


#5

maybe if you put subscription outside of router and instruct fast-render to send it in initial bulk ?


#6

Check this: https://kadira.io/academy/meteor-routing-guide/content/subscriptions-and-data-management


#7

Yeah, this probably makes the most sense, and also goes along with what Arunoda just posted as well. Keep the subscriptions in templates/components.

Thanks!


#8

Just my 2cents:

As I know eval is evil and you should avoid that.

You may use:

window[tmpl.component]

instead.


#9

Super pro tip, love it. Thanks!


#10

So I tried the following approach which doesn’t work (for obvious reasons):

lib/router.jsx

FlowRouter.route('/:slug', {
  action(params) {
    ReactLayout.render(TemplateLoader, {slug: params.slug});
  }
});

FlowRouter.route('/prs/:slug', {
  name: 'presentation',

  action(params) {
    let tmpl = Templates.findOne({slug: params.slug});

    if (tmpl) {
      ReactLayout.render(eval(tmpl.component));
    } else {
      ReactLayout.render(NotFound);
    }
  }
});

both/components/TemplateLoader/TemplateLoader.jsx

TemplateLoader = React.createClass({
  displayName: 'TemplateLoader',
  mixins: [ReactMeteorData],

  getMeteorData() {
    const handle = Meteor.subscribe('templates');

    return {
      ready: handle.ready(),
      template: Templates.findOne({slug: this.props.slug})
    }
  },

  componentDidMount() {
    if (this.data.ready) {
      FlowRouter.go('presentation', {component: this.data.template.slug});
    }
  },

  render() {
    return <div>
    </div>
  }
});

Obviously when FlowRouter.go is executed, we lose the subscription since it’s inside this component only. I really need some way to have the router be able to access the Templates collection directly.

I don’t really understand the Fast Render approach. It supposedly relies on the subscriptions property in Flow Router, which is being deprecated in 3.0. Is there some other way?


#11

I suppose another way to do this would be to pass the slug to the router, which would just pass that onto another template:

ReactLayout.render(Container, {slug: params.slug});

And then Container would subscribe to the templates, fetch the component name to render, and then maybe use React.cloneElement or React.createElement to render it.