Router.configure
waitOn: ->
Meteor.subscribe 'data'
Does data get torn down and re-subscribed to when I switch from /bar to /foo, or does it stay subscribed to for the entire session, like the null publication?
Yes, data is discarded when you change routes.
There are solution out there to solve this. One such solution is provided by #arunoda. Check out this repository: subs-manager
Usage with Iron Router: just replace Meteor.subscribe() calls with subs.subscribe(), where subs is a new SubsManager().
subs = new SubsManager
Router.configure
waitOn: ->
subs.subscribe 'data'
Related
It's evident how to provide route-specific data, i.e. through the use of a controller:
PostController = RouteController.extend({
layoutTemplate: 'PostLayout',
template: 'Post',
waitOn: function () { return Meteor.subscribe('post', this.params._id); },
data: function () { return Posts.findOne({_id: this.params._id}) },
action: function () {
this.render();
}
});
But how does one provide data for the application in general? In the case that every route needs to be subscribed to the same subset of information, so that the pub/sub doesn't need to be re-done on every route change. Thanks!
It sounds to me like you are looking for a completely general publication/subscription scheme so that you do not have to define the waitOn/data option combination for every single route or route controller that you define. In that case, you can simply publish a given set of data on the server like so:
Meteor.publish('someData', function() {
return SomeDataCollection.find({});
});
and subscribe to that set of data on the client like so:
Meteor.subscribe('someData');
With this publication/subscription pair setup, you will have access to the data provided in all routes. You just have to make sure that you check for non-existent data in your code to handle the first load of any given template when the data has not been loaded on the client yet. In this manner, you would never have to actually define a the waitOn and data options for any route or route controller.
If you would like to utilize Iron Router in a different way than through route controllers, you also have the option of waiting on one/many subscriptions globally by using the Router.configure({}); function. To use the example above:
Router.configure({
waitOn: function() {
return Meteor.subscribe('someData');
}
});
For information about this route option and all of the other options that you have available at a global level, check this out.
In the next phase of my Meteor journey (read: learning the ropes!), I'd like to implement a simple search based on user inputed values, then redirect to a route specific to the record returned from the server.
At the moment, I'm picking up the inputed values via this code:
Template.home.events 'submit form': (event, template) ->
event.preventDefault()
console.log 'form submitted!'
countryFirst = event.target.firstCountrySearch.value
countrySecond = event.target.secondCountrySearch.value
Session.set 'countryPairSearchInputs', [countryFirst, countrySecond]
countryPairSearchInputs = Session.get 'countryPairSearchInputs'
console.log(countryPairSearchInputs)
return Router.go('explore')
Happily, the console log returns the desired countryPairSearchInputs variable - an array of two ids. In my routes.coffee file I then have the following:
#route "explore",
path: "/explore/:_id"
waitOn: ->
Meteor.subscribe 'countryPairsSearch'
On the server side, I have:
Meteor.publish 'countryPairsSearch', getCountryPairsSearch
And finally, I have a search.coffee file in my /lib directory that defines the getCountryPairsSearch function:
#getCountryPairsSearch = ->
CountryPairs.findOne $and: [
{ country_a_id: $in: Session.get('countryPairSearchInputs') }
{ country_b_id: $in: Session.get('countryPairSearchInputs') }
]
With regards to the search function itself, I have a CountryPairs collection where each record has two ids (country_a_id and country_b_id) - the aim here is to allow users to input two countries, with the corresponding CountryPair then being returning.
I'm currently struggling to tie all the pieces together - the console output on searching is currently:
Uncaught Error: Missing required parameters on path "/explore/:_id". The missing params are: ["_id"]. The params object passed in was: undefined.
Any help would be greatly appreciated - as you can probably tell I'm new to Meteor and am still getting used to the pub/sub methodology!
Edited: mixed up client/server for the publish method when I first posted - the danger of late-night posting!
First, seems you're expecting an :id parameter on your 'explore' route.
If I understand you're case, you're not expecting any params here, so you can just delete ':id' from your route:
#route "explore",
path: "/explore/"
waitOn: ->
Meteor.subscribe 'countryPairsSearch'
or either add a params to your Router.go call:
Router.go('explore', {_id: yourIdVar});
Secondly, you're trying to use a client function: Session.get() server-side. Try to update the publication using a parameter ; or using a method.call.
client-side
Meteor.subscribe 'countryPairsSearch' countryA countryB
not sure about the coffeescript syntax, check http://docs.meteor.com/#/full/meteor_subscribe
and server-side
#getCountryPairsSearch = (countryA, countryB) ->
CountryPairs.findOne $and: [
{ country_a_id: $in: countryA }
{ country_b_id: $in: countryB }
]
I'm using Iron Router to pass data through to my templates:
#route 'singleProperty',
path: '/properties/:_id'
data: ->
Properties.findOne(#params._id)
controller: "SinglePropertyController"
And within my controller, I have my template waiting for the necessary collection to be published:
waitOn: ->
[
Meteor.subscribe "properties"
]
The issue that I am having is that when I try to access #data from within my helpers, it comes back as undefined:
Template.singleProperty.helpers
currentProperty: ->
console.log #data
That said, when I run the same console.log within a Template.rendered, I get the result that I would expect (the data object):
Template.singleProperty.rendered = ->
console.log #data
What do I need to change to be able to access data within a Template.helper?
Try this in your Template helper method:
Template.currentData()
Template.instance() is the key thing to read upon in the Meteor docs
hope this helps.
I have created a route like:
this.route('design.create', {
path: '/design',
template: 'Design',
I have wroten some waitOn, data, onBeforeAction, onStop related and executed when the route is loaded.
I have a "strange" use case:
Where I'm already in the route design.create I would like re-init it (execute again waitOn, data, onBeforeAction, onStop).
A simple: Router.go('design.create') not works...I guess because I'm already on the same route.
Seems that:
Router.current()._computation.invalidate()
Do what I want...but it's not very elegant call a private method
Set a dependency by using a session variable in the onBeforeAction and change the session variable when you what the current route to get rerun:
...
onBeforeAction: function() {
Session.get('dependOnMe')
// the acutal onBeforeAction-code goes here
}
...
...
// somewhere else
Session.set('dependOnMe', new Date())
// will trigger the onBeforeAction again
...
Why is it that .findOne() does not work when executed inside the Router?
It always returns undefined.
Yet .find() works without any problems. Also tested .findOne() by manually entering the condition and the ._id manually.
Is .findOne() not designed to work inside the Router?
It's working properly in my application. I implemented it like this:
Router.map(function() {
this.route('training', {
path: '/training/:id',
data: function() {
return Trainings.findOne({id: this.params.id});
},
notFoundTemplate: 'notFound',
title: "Training"
});
});
it works fine!
maybe you're trying to pass the result to an iterator? it's not a cursor.
try a find().fetch() and use the result in the same way.
if it's a data/timing issue you could also guard with a ready() function.
you don't need to if you're using it reactively but this gives you a bit more explicit knowhow when things happen, yet without using a waitOn.
in coffeescript:
#---------routes ---------
#route 'routeName',
path: '/path/to/:cname'
onBeforeAction: ->
Meteor.subscribe('Things', {
cname: #params.cname
})
this.next()
data: ->
if #ready()
data = {
params: #params
}
data.lesson = Things.findOne({cname: #params.cname})
return data