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

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}

Related

Meteor & Iron: Using tag from array as path

I'm using Iron Router for Meteor and I have these routes set up and working so far:
Router.map(function () {
this.route('home', {
path: '/',
template: 'home'
});
this.route('userProfile', {
path: '/user/:username',
data: function () {
return Meteor.users.findOne({username: this.params.username})},
template: 'userProfile'
});
this.route('postPage', {
path: '/post/:_id',
data: function () {
return Posts.findOne({_id: this.params._id})},
template: 'postPage'
});
this.route('settings', {
path: '/settings',
template: 'settings'
});
});
I'd like to introduce another route that would take you to a page where posts are organised by tag. For example http://foo.bar/tag/mexican would take you to a page displaying all the posts that have the tag mexican in an array. The posts are stored in the collection like so:
{
"_id" : "G6xrPYedPzG8KGBTF",
"title" : "Chilli Con Carne",
"rating" : 4,
"tags" : [ "kidneybeans", "mexican", "rice" ],
"description" : "A meaty mexican dish with rice.",
"createdAt" : ISODate("2015-02-15T23:45:12.384Z"),
"owner" : "vh7DjWM9NwbkGSNg9",
"username" : "snowingjack"
}
I've been able to make routes that pass data by post._id as you can see, but now I can't figure out how to make a route where /tag/:tag would take me to page with the data is the posts collection queried by tag. I'm fine running a query that will just return posts with a certain tag like return Posts.find({tags: "mexican"}); but I'm struggling with how to pass all this data around to get the desired effect.
Ideas?
P.S. I also have a template I use to spit out tags in a sane fashion with hashtags and spaces and links and whatnot. I'd love to be able to find a solution where this would work:
<template name="tagListItem">
<span class="tag-list__item">#{{this}}</span>
</template>
I think the route would look something like this:
this.route('tagPage', {
path: '/tags/:tag',
data: function () {
return Posts.find({tags: this.params.tag)},
template: 'tagPage'
});
And the anchor tag inside the template you use to output tag links would probably look like this ( though I'm not exactly sure the data context of the template you mentioned in your post):
#{{this}}
The other thing you would have to consider is how you are publishing and subscribing to posts. You might want to make a separate pub/sub relationship for tags so you are sending posts with tags you don't need down the line.

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

Updating a record and then displaying it in iron router function

I've got the following iron-router setup with meteor:
Router.map(function() {
this.route('decoded', {
path: '/id/:id',
data: function() {
message.update({_id: this.params.id._id}, {$set: {read: "1"}});
return message.findOne({id: this.params.id});
}
});
Take note that I am storing an id, which is not the same as _id that mongodb generates.
In my template I have {{subject}}
On insert, the value of read is 0, when they visit the url /id/:id I want the value of read to be changed to 1, and then display the subject.
When I run it, I don't get any errors or anything. It displays my message, just fine, but doesn't update the read field.

understand new mongo id and use it with iron-router

i have a simple post route that looks for the post _id.
The problem is that the pathFor helper creates a path like this:
ObjectID("52e16453431fc2fba4b6d6a8")
I guess the mongoDB insertion as been changed and now the _id object holds another object inside it called _str.
Here is my route:
this.route("post", {
path: "/post/:_id",
waitOn:function(){
NProgress.start();
Meteor.subscribe("Teams");
},
before: function () {
NProgress.done();
},
data: function () {
return Posts.findOne({_id: this.params._id});
}
});
Currently, it creates an href like :
post/ObjectID("52e16453431fc2fba4b6d6a8")
clicking on it opens a url
post/ObjectID("52e16453431fc2fba4b6d6a8")
However, I get the "NotFound" template instead of the post.
How can I fix this?
You need to change the pathFor 'post' to pass the hex representation of the ObjectId 52e16453431fc2fba4b6d6a8 instead of ObjectId('52e16453431fc2fba4b6d6a8')
Try something like this pathFor 'post' _id=this._id.toHexString
Once you are passing the hex string, you can use this in your router
return Posts.findOne({ _id: new Meteor.Collection.ObjectID(this.params._id)});
Can you try this:
this.route("post", {
path: "/post/:stringId",
waitOn:function(){
NProgress.start();
Meteor.subscribe("Teams");
},
before: function () {
NProgress.done();
},
data: function () {
Post = Posts.findOne({_id: Meteor.ObjectId(this.params.stringId)});
}
});
Now when you go to post/52e16453431fc2fba4b6d6a8 you should be able to see the correct post.
I am actually planning on using iron-router and objectid's in my application and thinking that this pattern would work.
I have not tried it yet, but let me know if there is a problem and I'll create a small test app to work it out.

backbone.js won't fetch data and populate the collection

I have this setup: require.js + backbone.js, that which populate the collection using fetch function of backbone
orders.js(collection)
define([
'underscore',
'backbone',
'models/item'
], function(_, Backbone, Item){
var Orders = Backbone.Collection.extend({
model: Item,
url: 'json/items',
initialize: function(){
},
});
return orders = new Orders();
});
orders.js(views)
define([
'jquery',
'underscore',
'backbone',
'collections/orders',
'models/item',
'text!templates/orders.tpl',
], function($, _, Backbone, Orders, Item, ordersTemplate){
var OrdersView = Backbone.View.extend({
model: Orders,
template: _.template(ordersTemplate),
initialize: function() {
_.bindAll(this);
Orders.fetch({ success: function() {
console.log(Orders.models)
}});
},
});
return OrdersView;
});
Orders.fetch won't populate the collection, though browser detects XHR json/items:
I already tried this solution Backbone.js + Rest. Collection is not populated after fetch() but still it won't work. Is there any way it can fetch data and populate it automatically to the collection? Or am I missing something?
PS: sorry for the brute code posting...
EDIT: success callback on fetch won't do anything but json/items just called by XHR on browser
EDIT: update code on order.js, removed the STORE param
EDIT: i appreciate if you can look on to this url http://mindanaojobs.net/backbone/ and inspect something in it, jsfiddle seems a little bit tricky
Does the XHR response contain an array of objects? If there is any kind of object wrapper like
{items: [{...}, {...}]}
then you need to implement the parse method accordingly.
Yes, I looked at your code and you need this in your Orders collection:
parse: function (response) { return response.items;}