Implementing a simple search with Meteor and Iron Router - meteor

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 }
]

Related

Publishing role names into a table Meteor alanning:roles and aldeed:tabular

I'm having trouble displaying the created roles in a table. I'm using alanning:roles and aldeed:tabular. To create the table I have:
TabularTables.RolesAdmin = new Tabular.Table({
name: "Roles",
collection: Meteor.roles,
pub:"rolesadmin",
allow: function (userId) {
return Roles.userIsInRole(userId, 'admin');
},
columns: [
{
data: "name",
title: "Role Name",
},
],
});
And the publication looks like this:
Meteor.publish( 'rolesadmin', function() {
return Meteor.roles.find( {}, { fields: { "name": 1 } } );
});
When running the app the table only displays "Processing..." thus there is an error and it is not able at access/find the data?
I'm getting the following exception in the server terminal:
Exception from sub rolesadmin id 6c6x3mDzweP8MbB9A
Error: Did not check() all arguments during publisher 'rolesadmin'
If I check in mongo db.roles.find(), there is no role with 6c6x3mDzweP8MbB9A id. What does this error refer to?
From the meteor-tabular docs:
To tell Tabular to use your custom publish function, pass the
publication name as the pub option. Your function:
MUST accept and check three arguments: tableName, ids, and fields
MUST publish all the documents where _id is in the ids array.
MUST do any necessary security checks
SHOULD publish only the fields listed in the fields object, if one is > provided.
MAY also publish other data necessary for your table
So it looks like you'll need to account for those three arguments mentioned in the documentation appropriately. I'm not sure you actually need a custom pub for this, though, based on what you are publishing.

Iron Router: How to Provide Same Data to Multiple Routes

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.

Meteor.js 1.0 - Data context involving Iron Router and template helpers

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.

Meteor Iron-Router: why isn't this routing setup (with waitOn and data functions) working correctly?

Using Iron-Router 1.0.3 and Meteor 1.0.
I have this route defined:
Router.route('audit', {
path: '/audit/:audit_id/',
template: 'audit',
data: function() {
audit = Audits.findOne({_id: this.params.audit_id});
lineitems = LineItems.find(JSON.parse(audit.query));
return {
audit: audit,
lineitems: lineitems
}
},
waitOn: function () {
return [
Meteor.subscribe('audits'),
Meteor.subscribe('lineitems', this.params.audit_id),
]
}
});
Objects in the Audits collection have the following structure:
{
_id: 'timestamped-id-that-I-generate',
name: 'some name',
query: JSON.stringify({'$and': [list of query conditions here]})
}
When I go to this route I get the following error in my console:
Uncaught TypeError: Cannot read property 'query' of undefined
But if I go to my browser console, I can examine the Audits collections and I see that the Audit object with the appropriate _id and query exists as expected.
Incidentally, the publish method for the lineitems on the server side will simply look up the same audit object and publish the line items that match it's query.
(I have to serialize the query back and forth via JSON because otherwise the query object would contain field names that violate MongoDB regulations.)
As of this writing, Iron Router's data hook may run multiple times when a route is evaluated. You can assume it will run once before the subscriptions are ready, and again afterward. Because it will run prior to your subscriptions being ready, you need to assume that Audits.findOne will return undefined at least once. You have two choices:
Check for this.ready() in your data hook:
data: function() {
if (this.ready()) {
audit = Audits.findOne({_id: this.params.audit_id});
lineitems = LineItems.find(JSON.parse(audit.query));
return {audit: audit, lineitems: lineitems};
}
}
Use a guard:
data: function() {
audit = Audits.findOne({_id: this.params.audit_id});
if (audit && audit.query) {
lineitems = LineItems.find(JSON.parse(audit.query));
return {audit: audit, lineitems: lineitems};
}
}
A combination of the two may actually be appropriate if: (a) you don't have any audits, or (b) not all audits have a query.

Proper way to .findOne() in Iron:router

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

Resources