I’m glad I no longer have to recommend something that starts with meteorhacks
anymore
Just a heads up, in React I think it is a best practice to wrap JSX in parentheses, like so:
BlogHome = React.createClass({
render() {
return (
<div>
<p>This is the home page of our blog</p>
<p>
<a href="/hello-world">See Hello World Post</a>
</p>
</div>;
)
}
});
Thanks for the advice. I’ll follow this.
Thanks arunoda for your work.
I’m excited to check these out!
Edit:
Btw, this is great news!
FlowRouter 1.x comes with its own subscription registration API. It’s much better than managing subscriptions at the router level. Anyway, we are removing it in 3.x.
Hey @arunoda,
I didn’t see this in the migration guide: After installing FR2, the first argument passed to the action
function no longer contains the query
parameters. I used to be able to do params.query.id
where params
is arg1 to action
. The guide does mention something similar for the FlowRouter
object methods, though.
The query params seems to be the second argument to the action
function now.
If this was in FR1, and I was using an old version, my mistake. Thought I should mention it just in case, though.
Thanks!
Can I ask why is subscription mgmt in the router considered an anti-pattern?
I am not trolling, I think I must be missing the point. I am looking to understand why.
The points you raised I have never had an issue with with my apps (maybe I’ve just not noticed).
To be honest I don’t quite understand the tracker autorun means, is that bad?
I’ve not had reactivity issues, but I don’t really have anything reactive in my subscriptions - the user subscribes to their data and unless they log out that subscription just updates with their data…
The point on “loading messages” hasn’t been an issue, I simply defined a global helper which checked the subs.
From that I could toggle a loading message… simple enough.
// subReady - true/false for subscriptions being ready
Template.registerHelper('_subReady', function(subName) {
if(subName) {
return FlowRouter.subsReady(subName);
} else {
return FlowRouter.subsReady();
}
});
I find great utility in grouping routes with flowrouter v1.
With that grouping I can define my subscriptions to the group.
If I had to do it on the template level I would be repeating myself with each template in that group?
Am I missing something??? To me it seems that the router is just as appropriate for sub mgmt as templates are…
edit:
as always thanks for the awesome contribution with v2
I am looking forward to updating my apps with it
Two problems that result from doing subscriptions and data management in the route:
- Showing a loading screen for the whole page at once, instead of per-component. The best thing to do is to show as much of the page as possible immediately, and only have loading indicators inside components which are actually loading new data. Iron router convinced everyone to basically show a loader for every since page in the app, which means you lose most of the benefits of having a client-side app in the first place.
- Having to specify the data you are loading far away from the components which use the data. It’s easy to add a new component to your page and forget to add the data loading to the router, or the other way around where you load a bunch of data in the router that you actually aren’t using anywhere on the page.
The subscription is the middleman between the view and the data. I don’t want my view to change because the middleman changed. I want it to change only when the data changed.
The way I think about it is, you want the granularity of data to be as close to the granularity of rendering as possible. If you get new data, only re-render the html that depends on the new data. It’s hard to do that with reactive route level subs. For sending necessary initialization information to the client (e.g. user profile, or user notification info that is not dep. on routes) imho it’s better to do that outside of routes too because that’s global and simply depends on if they are a connection client or not; no route level granularity.
Edit:
By doing things at route level, you lose all the specificity that you would have had at the view level.
Actually, it was mentioned at the end. Very last line.
You mean this?
Earlier, you can access query params with FlowRouter.current().params.query. But, now you can’t do that. Use FlowRouter.current().queryParams instead.
I didn’t know they boiled down to same implementation:
//in route
action: function(params){
params.query // no longer supported
//where is queryParams?
}
I just did a simple console(arguments)
to see that you changed it to be the second argument.
Actually, queryParams
second argument was there.
Ah i see! ok well that makes sense, I guess i just got used to using the first arg in the old API - didn’t even know it was provided as a second argument. Maybe I did, but forgot since i never used it.
Even we used it. I don’t remember where we used for that API. May be from NodeJS express I think.
It’s a great idea for a small app (like the leaderboard example). However it’s easier to reason about data flowing in a large app when you break it into many small pieces.
Think of the facebook app and it’s chat sidebar. You could have the top-level chat-sidebar template subscribe to the chats and if you want to adjust the chats subscription… you can drill right down into it (Facebook also uses a template called a ‘container’ to let the dev know it’s handling data).
This moves several hundred lines of code in a router action down to each component that ‘owns’ the part of the app. Everything the sidebar needs is right there (not spread out across other parts of the app).
Thanks everyone for the answers.
I guess I didn’t see it from that point of view because I have only built simple single page applications which don’t have multiple templates rendered on the screen. Also in my apps I usually only have two or three collections which get subscribed to.
I might try to refactor my code a bit to try and move that logic out of the router and into the template to see how it might work. To be honest, I never tried it, just thought about it.
I think your code can be reduced a bit:
Template.registerHelper('_subsReady', FlowRouter.subsReady);
well, that 1 do not handle specific subscription parameter
And there is already default one.
{{ #if Template.subscriptionsReady }}
I have been looking at flow router today. I have a template with params all over it from one subscription. Do I need to wrap each one with an ifReady statement?
<template name="recordView">
<div class="container">
<div class="page-header">
<h1>{{ title }}</h1>
<div id="recordMeta">
<i class="fa fa-clock-o"></i> {{date}}
</div>
<div id="recordCounts">
{{#if commentCount}}
<i class="fa fa-comment fade"></i> {{commentCount}}
{{/if}}
<i class="fa fa-heart fade {{#if faveToggle}}pullFave{{ else }}setFave{{/if}}"></i> {{faveCount}}
</div>
</div>
<div class="row">
<div class="col-md-6">
<div id="recordText">
{{ text }}
</div>
<div id="recordComments">
{{> commentsList }}
{{> commentForm }}
</div>
</div>
<div class="col-md-6">
<div id="recordMedia">
</div>
{{#if loc}}
<div id="mapView" class="mapbox"></div>
<pre id='coordinates' class='ui-coordinates'></pre>
{{/if}}
</div>
</div>
<div class="row">
{{#if isInRole 'admin'}}
<a href="/archive/{{ _id }}/edit" class="btn btn-info btn-small pull-right recordEditButton">Edit</a>
{{/if}}
</div>
</div>
</template>
You could just add {{#if Template.subscriptionsReady}}
as the second line (and the necessary {{/if}}
at the end). This for each template, which might seem weird at first when moving over from Iron Router. The thing is that Flow Router gives you a lot of freedom. I have a special Template helper which I can use to check whether a specific subscription is ready or not. This means that if the client creates 3 new subscriptions, I can check each one specifically in each component, ending up with a much better UX.
The routes themselves also end up being really thin, compared to the fat ones in Iron Router - the result is much better code and functionality separation. Less bugs, less weird interaction, less headaches, etc.