how to keep a subscription open between routes? - meteor

How do i keep a subscription open between different calls to the IronRouter routes?
I have an app that has a hierarchy like a magazine, issue/page
content/:issue/:page
so this would be:
content/may/page1
content/may/page2
content/may/page3
I would like to load all the content for "May" issue within the router, and when the pages are changed NOT do an unsubscribe. I am seeing some flicker with the app which I think is caused by delay as the data is being reconnected to.
I currently have a simple Content.find({}) but there is an apparent delay seeming to be caused by the unsub/pub/sub handshake - even though the content is not changing.
Update: I moved the subscribe outside of IronRouter routes entirely and now it seems "sticky". However i imagine subscriptions are getting unsubscribed somewhere, so I'm not sure if this is the best solution. It is also now a global subscribe" whereas really i want to be able to control pub/unsub only if the :issue parameter has changed on that route.

Handling subscriptions on a template level is far superior to handeling it in the router.
Because when you handle it in the router the page only gets loaded after the subscription is ready. Which makes your application feel less responsive.
Template:
<template name="example">
{{#with data=getData}}
{{#if $not data.ready}}
{{> spinner}} Loading...
{{/if}}
{{/with}}
</template>
Template helper:
Template.example.getData = function() {
var ready = Meteor.subscribe('someSubscription', this.issueId).ready();
var data = collections.example.find();
return {
ready: ready,
data: data
};
};
This results in a more responsively feeling application. It is always better to avoid a slow/bad/nonresponsive design, before resorting to something like caching to fix a problem with the used design.

Related

Is there an elegant way to refresh a given template with Iron Router?

Since my variables are not reactive and there are probably over 500 variables in my script, there is not feasable way I can make everything reactive right now.
So my question is, is there any way to make Iron Router refresh the current route/template when prompted? Right now, the only way I accomplish this is using document.location.reload(true);, but that is an ugly looking and very slow process.
Let's also say that I have a {{> yield}} element in my template: is there any way to just refresh that part without refreshing the entire page?
You probably need to define a reactive variable (Session or ReactiveVar) and change the state of that variable. On your template, make an if wrapping the part you need to refresh. When you change the state, the part inside the if will be refreshed.
helpers:
h_checkToRefresh: function() {
if (Session.get('count') > 0) return true;
else return false;
}
template
{{#if h_checkToRefresh}}
{{>yield}}
{{/if}}

Iron router sublayouts - getting data context on yield

Consider the following templates:
layout
pageLayoutStandard
aboutUs
'layout' is my top-level template, which I specify using:
Router.configure({
layoutTemplate: 'layout',
});
Inside layout.html I have the following:
<main id="site-main" role="main">
{{>Template.dynamic template=page.pageLayoutTemplate }}
</main>
I pass some data in from the route: a page object which has a property 'pageLayoutTemplate', having the value 'pageLayoutStandard'.
Inside 'pageLayoutStandard' template, I have:
{{> yield }}
If I visit the '/about-us' route, I render the 'aboutUs' template into 'pageLayoutStandard' - no worries.
And now to my problem...
In my 'aboutUs' template, I expect the 'data' property of the instance to contain the data I passed down from iron-router. However, I find that my the data property contains a 'Template' object; specifically, it contains 'pageLayoutStandard'.
So it looks like 'yield' doesn't like living in a sub-layout - it wants to live at the top level layout for it to get the data from the route. This I validated by moving my yield to the top level layout - the 'aboutUs' template then gets the right data.
Is there any way I can get 'yield' to get the correct data context when it exists in a sublayout?
One solution is to access the data using
Router.current().data()
I my self am fairly new to Iron-Router, but I believe it may be a similar issue that I cam across in another way. There are also several ambiguities in your question, such as where and how are you specifically defining the data context.
Basically I discovered that {{> yield}} creates it's own <body> tags. This also means that things like Template.body.events(); don't propagate into this new Iron-router <body>. This is a known "bug" with Iron-Router.
There is a workaround that has been developed to solve that particular issue, but I'm not sure it's relevant to your case, or at least may not solve the problem since you are not looking to propagate the main body template.
In the end it may be that your routing logic is somewhat inverted, as you mentioned, with the intended usage of Iron-Router.
I believe a better way to perform what you want would be to have:
<main id="site-main" role="main">
{{> yield}}
</main>
With something like this in your router definition
Router.map(function (){
this.route("/about-us", {
template: "pageLayoutStandard"
}
});
You should then be able to set your data context and rendering as per usual.

Iron router slow to change routes

When clicking a link, or loading via console with Router.go(...) there is a noticeable pause between calling the route and my app doing anything.
There are no DDP calls being made during this pause and I added debugging to all my templates for rendering and no re-rendering is taking place. I can't for the life of me figure out what is causing this pause, you can see this in action at http://riustats.com
Bassed on #below9k comment, seems like you are trying to load big amount of data in the desired route.
Be sure don't have some observersChanges also.
A good solution could be using the fast-render package.
Code Example.
Router.route('leaderboard', {
waitOn: function(){
return Meteor.subscribe('leaderboard'); //you big collection.
},
fastRender: true // important to render the route fast.
});

Spacebars-generated dynamic links do not trigger Iron Router in Meteor?

I've got this in my routes:
Router.route('/videos/:id', {
name: 'VideoPage',
data: function(){
return Videos.findOne(this.params.id);
}
})
This template shows up at the route above:
Template.VideoPage.helpers({
'videoIds': function(){
var myVideoIds = [
"23456",
"h5e45t",
"f4e4w",
"g6h4h"
];
return myVideoIds;
}
});
HTML:
<template name="VideoPage">
{{videoTitle}}
<p>Click the links below to get to a new video page</p>
{{#each videoIds}}
<a href="/videos/" + {{this}}>
{{/each}}
</template>
When I click on a link, the URL in the browser changes from something like /videos/23456 to something like /videos/f4e4w, but the request never actually goes through Iron Router and the Router.route function. I put a debugger before Router.route and it triggers on initial page load but does NOT trigger when the links are clicked.
I understand that Iron Router's default behavior is NOT to re-render a template the user is currently on, but this is the same template with different url params that are used to change the data in the template, so IMO it should still re-render.
Ok, false alarm. It appears that Router.route DOES fire and updates the data context every time the params._id changes. I was placing the debugger outside of the Route function when I should have been placing it inside of the data context function.
The example that I gave in this answer was also a highy, highly simplified example. It wasn't working in my real-life project due to something else (a complex video iframe generator) that was being refreshed improperly.
But rest assured that going from /videos/f4e4w to /videos/23456 by clicking a link DOES still go through Iron Router and the params does get read and the data does get updated.

Need help pls: Meteor and Famous integration and creation of forms

I am currently using Meteor 0.9.2.2. I am trying to better understand how to build a form in Meteor + Famous without having to put each form element into a Famous surface.
I am using the "gadicohen:famous-views 0.1.7" and "mjnetworks:famous 0.2.2 "
I am using https://github.com/gadicc/meteor-famous-views and have looked at some of the samples of events. I can generate events on the view but seems to have lost the ability to generate events using Jquery (probably Famous alarm bells going off) for fields on the template.
(Note I fid try following What is a recommended way to get data into your meteor template from the front-end for a famous surface? but that just directed me to the examples I am following - sorry still stuck)
For example, if I wanted to have a "blur" event when a contenteditable field changed and used that to update the database, I am not sure how I do it.
BTW, I am bringing in the template via Iron-router:
this.route('someTemplate', {
path: '/',
});
Here's some sample of code of what I have been playing around with:
<template name="someTemplate">
{{#Scrollview target="content" size="[undefined,100]"}}
{{#Surface class="green-bg"}}
<h4 id="main-edit-title" class="editable" data-fieldname="title" data-resourceid="{{_id}}" contenteditable=true>Heading</h4>
<p id="main-edit-message" class="mediumEditable editable" data-fieldname="message" data-resourceid="{{_id}}" contenteditable=true>Summary</p>
{{/Surface}}
{{/Scrollview}}
</template>
Template.someTemplate.events({
'blur .editable': function (e) {
e.preventDefault();
//e.stopPropagation();
var item = $(e.currentTarget);
DO SOME UPDATE STUFF TO THE DATABASE
item.removeClass('resourceUpdated');
},
});
I looked at the 'famousEvents' too and could not seem to get that working. Ie no events fired and that would only be at the surface level, not the field level.
At the view level I was fine and code below worked fine:
Template.someTemplate.rendered = function() {
var fview = FView.from(this);
var target = fview.surface || fview.view._eventInput;
target.on('click', function() {
clickeyStuff(fview);
});
}
I tried the other variants from this page: https://famous-views.meteor.com/examples/events
So the core questions, I think, is: Do I have to move every form element to a Famous Surface? This would be a killer. I am hoping I can still use Jquery or access the DOM for stuff within the template. Note I do see stuff in the Famous FAQ http://famo.us/guides/pitfalls that says don't touch the DOM... so happy to find out how else I should be doing this???
I tried to make this clearer on the events example page, but I guess I'm still not there yet. I'll answer below but please feel free to chime in with how I can improve the documentation.
Inside of a Surface is basically regular Meteor. But outside of a Surface is the realm of famous-views. So you need to have a Meteor template inside of a Surface for events to attach themselves properly - and, as noted in the docs - that template needs to have at least one element in side of it to attach the events. So either (and in both cases, renaming the outer template wrapper but leaving Template.someTemplate.events as is):
<template name="someTemplateWrapper">
{{#Scrollview target="content" size="[undefined,100]"}}
{{#Surface class="green-bg"}}
{{> someTemplate}}
{{/Surface}}
{{/Scrollview}}
</template>
or simply:
<template name="someTemplateWrapper">
{{#Scrollview target="content" size="[undefined,100]"}}
{{>Surface template="someTemplate" class="green-bg"}}
{{/Scrollview}}
</template>
and then move all the Meteor stuff that needs events to it's own template where the events are handled:
<template name="someTemplate">
<h4 id="main-edit-title" class="editable" data-fieldname="title" data-resourceid="{{_id}}" contenteditable=true>Heading</h4>
<p id="main-edit-message" class="mediumEditable editable" data-fieldname="message" data-resourceid="{{_id}}" contenteditable=true>Summary</p>
</template>
Hope that makes sense, just rushing out... let me know if anything is not clear and I'll ammend the answer later.

Resources