Creating a data context with a data option in iron-router not working - mongodb

Whilst using iron router 1.011 with Meteor I created a router which returns a Mongo db cursor to a collection subset using a route parameter; see below:
Router.route('/categories/:slug', function() {
this.render('CategoryShow', {
data: {
category_products: Products.find({
category: this.params.slug
})
}
})
});
This works fine, however I didn’t see this as particularly elegant and decide to pass the options as an object as per the documents; see below.
Router.route('/categories/:slug', {
data: function() {
templateData = {
category_products: Products.find({
category: this.params.slug
})
}
return templateData;
},
name: 'category.show'
});
This does not return any data to the template but I notice that this pattern seem to work find when using findOne() instead of find(). Note that the template is declared as:
<Template name="CategoryShow">
// bla bla
</Template>
Please advise

Related

How to show data from mongoDB with ObjectID

i have an "back end" application which write in MongoDb (in database i have _id: with ObjectId("13f6ea...002")) i use meteor app to show information. Everything was good i displays list of information with {{#each}}. But when i wanted show one element with '_Id' nothing works.
I read this issue and adapt my code to get right root, But i can't display anything on the page. I tried to write Template helpers but it didn't helped
Db record:
{
_id: ObjectId("13f6ea...002"),
url: "foo",
title: "bar",
published: "2014-08-22 03:26:21 UTC",
image: "foo.jpg",
summary: "foo ",
categories: [
"F",
"B"
],
...
}
Route:
this.route('news', {
path: '/news/:_id',
template: 'news',
waitOn: function () {
var id = this._id;
Meteor.subscribe('news', id);
},
data: function() {
var id = this.params._id;
return News.findOne({ _id: Meteor.Collection.ObjectID(this.params._id)});
},
action : function () {this.render();},
});
Publish
Meteor.publish('news', function(id) {
return News.find({_id: id});
});
Template which redirect to unique post
<h4>{{title}}</h4>
And template is just {{news}}
How can i fix this?
UPDATE
My solutions to fix that:
router.js
waitOn: function () {
var id = this._id;
Meteor.subscribe('News', id);
},
data: function() {
return News.findOne(new Meteor.Collection.ObjectID(this.params._id));
},
and in template
<a href="news/{{_id._str}}">
Navigate to the appropriate url in your browser (i.e. localhost:3000/news/[_id]), open the console and enter:
Router.current().data()
That will show you the data context of the current route. Either it returns nothing, in which case there is a fundamental problem with your News.findOne query as it's returning nothing, or (more likely) it returns the required document.
In the latter case, as far as I can see there is no news property within that document, which is why it isn't rendering anything. If you change {{news}} to {{url}} or {{summary}} I would imagine it would render the requested property.
If by {{news}} you're trying to render the entire document, then (aside from the fact that it will render as something like [Object]) you need to make news a property of the object returned by your data function:
return {
news: News.findOne({ _id: Meteor.Collection.ObjectID(this.params._id)});
};
Getting Document with _id :
In the .js file under events, Say on click event and Collection EventList :-
'Submit form' : function () {
var id = this._id;
return EventList.find({_id : id}).fetch();
}
This would return the object for the id. In my Case, I am displaying a field for all documents in Collection. User selects a record and clicks Submit, which fetches all Document fields and displays to the User

Meteor returning a field from MongoDB works in console but not in application

I am trying to read out a file in my MongoDB database. In the console the response is correct while in my application I get the following error:
Uncaught TypeError: Cannot read property 'iati' of undefined
I defined a template helper which should return a certain sub-field within my MongoDB collection. However the following does not seem to work (I get the beforementioned error).
Template.hello.helpers({
test: function() {
return Test.findOne().iati;
}
});
What does seem to work is to return the entire object:
Template.hello.helpers({
test: function() {
return Test.findOne();
}
});
And then call the specific field within the template:
{{test.iati}}
However, I want to use the data within the JavaScript script. What am I doing wrong?
Collection methods like Tests.findOne() return the documents that are already fetched to the client's Minimongo copy. Before your document is fetched, findOne() will return null.
To safeguard against this, simply check the result in the helper before you proceed with the calculation:
Template.hello.helpers({
test: function() {
if(! Test.findOne()) return;
return Test.findOne().iati;
},
});
You can also wait for the subscription in the Iron Router to ensure the proper documents are loaded:
this.route('routeName', {
...
onBeforeAction: function() {
this.subscribe('subscriptionName').wait();
...
},
...
});

How to read a collection that depends on another one in Meteor

I'm trying to load the latest post from a collection and at the same time all of the comments of that same post. The collection have references instead of storing the whole documents inside each other:
Post { title, body, etc..}
Comment { postId, body, etc.. }
I'm using iron-router as the routing package and in the route of my page I'm subscribing with this way:
this.route('home', {
path: '/',
template: 'home',
waitOn: function () {
return [
Meteor.subscribe('latestPost'),
Meteor.subscribe('lastReadPost')
];
}
});
The code that retrieves the post is simply:
Posts.findOne({}, {sort:{createdAt:-1, limit:1}});
Now the problem is that I don't know how to retrieve the comments without reading the whole collection. I can't subscribe in the router as I still do not have the post ID to query the Comments collection.
I guessed I could do that from the Template, but of course if I query the Comments collection, it's still empty. But I do have the postId as it's inside the Posts collection at that time. But I would need to trigger a subscription from the Template and that doesn't sound like a clean solution.
What would the best practice be? Thanks!
Server side code:
Meteor.publish("latestPost", function () {
var post = Posts.find({}, {sort:{created:-1}}).fetch()[0];
console.log("publish : " + post.title);
return [
Posts.find({_id: post._id}),
Comments.find({postId: post._id})
];
});
Client side code:
this.route('home', {
path: '/',
template: 'home',
waitOn: function () {
return [
Meteor.subscribe('latestPost')
];
},
data:function(){
return {
post:Posts.findOne(),
comments:Comments.find()
};
}
});
Check this repository to see whole example.
After user changes to another route, then subcriptions are being automatically stopped.
I would also include a limit in the server side finder options
{sort : {created : -1}, limit : 1}

How do I use new Meteor.Collection.ObjectID() in my mongo queries with meteor?

I have a Collection that has documents with an array of nested objects.
Here is fixture code to populate the database:
if (Parents.find().count() == 0) {
var parentId = Parents.insert({
name: "Parent One"
});
Children.insert({
parent: parentId,
fields: [
{
_id: new Meteor.Collection.ObjectID(),
position: 3,
name: "three"
},
{
_id: new Meteor.Collection.ObjectID(),
position: 1,
name: "one"
},
{
_id: new Meteor.Collection.ObjectID(),
position: 2,
name: "two"
},
]
});
}
You might be asking yourself, why do I even need an ObjectID when I can just update based off of the names. This is a simplified example to a much more complex schema that I'm currently working on and the the nested object are going to be created dynamically, the ObjectID's are definitely going to be necessary to make this work.
Basically, I need a way to save those nested objects with a unique ID and be able to update the fields by their _id.
Here is my Method, and the call I'm making from the browser console:
Meteor.methods({
upChild: function( options ) {
console.log(new Meteor.Collection.ObjectID());
Children.update({_id: options._id, "fields._id": options.fieldId }, {$set: {"fields.$.position": options.position}}, function(error){
if(error) {
console.log(error);
} else {
console.log("success");
}
});
}
});
My call from the console:
Meteor.call('upChild', {
_id: "5NuiSNQdNcZwau92M",
fieldId: "9b93aa1ef3868d762b84d2f2",
position: 1
});
And here is a screenshot of the html where I'm rendering all of the data for the Parents and Children collections:
Just an observation, as I was looking how generate unique IDs client side for a similar reason. I found calling new Meteor.Collection.ObjectID() was returning a object in the form 'ObjectID("abc...")'. By assigning Meteor.Collection.ObjectID()._str to _id, I got string as 'abc...' instead, which is what I wanted.
I hope this helps, and I'd be curious to know if anyone has a better way of handling this?
Jason
Avoid using the _str because it can change in the future. Use this:
new Meteor.Collection.ObjectID().toHexString() or
new Meteor.Collection.ObjectID().valueOf()
You can also use the official random package:
Random.id()

Meteorjs display referenced document field

I have the following scenario. There is a collection Suppliers and another Invited. Now Invited.supplier = Supplier._id (syntax might be wrong) Invited collection refers to Suppliers in One to Many fashion.
In my html , I have
<template name="mytemplate">
{{#each invited_list}}
{{supplier}}
{{f1}}
{{f2}}
{{/each}}
</template>
I have a helper function
Template.mytemplate.helpers({
invited_list : function(){
return Invited.find({"something"});
}
});
I would like to display {{Suppliers.name}} instead of _id in {{supplier}} in my invited_list . What are my options?
You could create a resolver function such as:
Template.mytemplate.helpers({
invited_list : function(){
return resolveSupplierToNames(Invited.find({"something"}).fetch());
}
});
function resolveSupplierToNames(invitedList) {
for (var i=0; i<invitedList.length; i++) {
invitedList[i].supplier = Suppliers.findOne({_id: invitedList[i].supplier}).name;
}
return invitedList;
}
There are generally two options with mongodb, one is the above (manual). The second is to use DBRefs. However I'm not sure meteor supports DBRefs completely yet. As suggested in the mongodb docs theres nothing wrong with doing it manually.
Update
Meteor has since introduced a transform function, you can do something similar like:
Template.mytemplate.helpers({
invited_list : function(){
return Invited.find({"something"},{transform:function(doc) {
doc.supplier_name = Suppliers.findOne({_id: doc.supplier_id}).name;
return doc;
});
}
});