Meteor & Iron: Using tag from array as path - mongodb

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.

Related

React Component iterate/loop properties of Mongo object

I do have a Mongo collection that stores albums with predefined "slot" for its images and feel a bit stuck if there a way to loop over the properties of the collection in order to display images in separated divs.
I did used this code for mapping over the album covers and it worked great:
albums() {
return Albums.find().fetch();
}
{this.albums().map( (album) => {
return <div key={album._id}><img src={album.cover} /></div>
})}
But now I ask you to help, is it possible to loop over photoOne, photoTwo, etc... and skip/don't display data if it is empty like in photoThree for example.
{
"_id" : "CHMHbNWWwZGaLGvB6",
"title" : "Text",
"cover" : "link",
"createdAt" : date,
"photoOne" : {
"titleOne" : "Text",
"coverOne" : "link"
}
"photoTwo" : {
"titleTwo" : "Text",
"coverTwo" : "link"
}
"photoThree" : {
"titleThree" : "",
"coverThree" : ""
}
}
I'm not a Mongo user, but in the map function you can check for the existing values, and handle it there. Something like (there is surely a cleaner way, though):
this.albums().map( (album) => {
for (key in album){
if (key.startsWith('photo')){
var title = Object.keys(album[key])[0];
if (album[key][title].length != 0){
console.log("Can use: " + Object.keys(album[key])[0])
}
}
}
})
Results in:
Can Use This: titleOne
Can Use This: titleTwo
Hope that helps, but it seems like having the photos with photoOne, photoTwo you are limiting the number of photos to use and requiring the need to use Object.keys to get the values out (without specifically using album.photoOne, album.photoTwo, etc.
If the album photos were stored in an embedded document, you could just include the photos and titles that existed and avoid having to check for empty ones. You would just loop through the photos that are present....if that makes sense.

howto route with collection's param in iron router Meteor JS

It is common way to route collection News as below using _id params,
this.route('newsPage', {
path:'/news/:_id',
data: function(){ return News.findOne(this.params._id);}
});
when News collection is declared like this
{ "_id" : ObjectId("51a7dc7b2cacf40b79990bf7"), "name" : myName, "body": mybody }
I am trying to route like this.
this.route('newsPage', {
path:'/news/:name',
data: function(){ return News.findOne(this.params.name);}
});
so I can route /news/myName. in MongoDB _id is unique, my News collections name params are unique too. Is it possible to route like this.
data: function(){ return News.findOne({name: this.params.name});}
Yup, it should be. If the names are unique, I don't see a problem why this would cause any problems.

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

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.

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}