Related Posts In Ghost Blog - Excluding Current Post - ghost-blog

I'm using Ghost as blogging platform. When a user is reading a post I would like to show some related posts.
{{#foreach tags limit="1"}}
{{#get "posts" filter="tags:{{slug}}" limit="6" include="author,tags" as |article|}}
{{#foreach article}}
....
{{/foreach}}
{{/get}}
{{/foreach}}
I managed to get related posts, but I'm having issues deleting the current post from the results.
According to the Ghost Documentation I should be able to use this addition to the filter:
"+id:-{{post.id}}"
Like this:
{{#get "posts" filter="tags:{{slug}}+id:-{{post.id}}" limit="6" include="author,tags" as |article|}}
Unfortunately this is not working, {{post.id}} doesn't even prints out anything regardless the scope I am in. Simply using {{id}} instead of {{post.id}} I'm getting a value but it's the tags ID so that's not correct.
I managed to access my post ID inside the tag scope this way {{../id}} but I cannot use it in the filter this way, it's not working either.
Any idea on how to solve it would be appreciated.

I think just {{id}} would work. Just tried this and it works by removing the id of the current post but leaving the others:
{{#get "posts" limit="all" filter="id:-{{id}}"}}
{{#foreach posts}}
{{id}}
{{/foreach}}
{{/get}}
Would have to be inside your {{#post}} scope though.

I achieved what I needed using jQuery.
First I get the current post id calling my function, fetching the tags for that post.
function getCurrentPosts(id) {
$.get(ghost.url.api('posts', {filter: 'id:' + id, include: 'tags'})).done(function (data){
var tags = data.posts[0].tags;
getRelatedPosts(id, tags)
}).fail(function (err){
console.log(err);
});
}
Then I get all the posts which have the same tag and have different id from my current post id.
function getRelatedPosts(id, tags) {
tags = tags.map(function(obj){
return obj.slug ;
}).join(', ');
tags = '[' + tags + ']';
$.get(ghost.url.api('posts', {filter: 'id:-' + id + ' +tags:' + tags, include: "author, tags" })).done(function (data){
var posts = data.posts;
console.log(posts.length)
if (posts.length > 0) {
showRelatedPosts(posts);
}
else{
$('.related-section').hide();
}
}).fail(function (err){
console.log(err);
});
}

Related

How do I control two subscriptions to display within a single template?

Sorry kind of new to the Meteor framework!
I Subscribed to two Publish functions. Even if both publish functions target the same Collection, they both have different functions, that I would like to display in one template. How do I achieve this. I have done allot of research but there doesn't seem to be sufficient information on how to achieve.
Following are the two publish functions in code that I subscribe to:
.server/main.js:
Meteor.publish('MerchantTrending', function (categoryMan){
var currentUser = this.userId;
return buyList.find({ who:"Merchant", ownerId:currentUser, itemCategory: { $in: categoryMan } }, {skip: 0, limit: 3});
});
.server/main.js:
Meteor.publish('myTopViews', function (){
var currentUser = this.userId;
return buyList.find({ newArrivalsExpiryDate : {'$lte': new Date()}}, {ownerId:currentUser }, {skip: 0, limit: 3});
});
Following is the subscription function in code
.client/main.js:
Router.route('/MerchantLandingPage', {
subscriptions: function(){
var categoryMan = Session.get('category');
return Meteor.subscribe('MerchantTrending', categoryMan, 'merchantTopViews')
}
});
Now the helper function in code:
Template.MerchantLandingPage.helpers({
'top3Trending' : function () {
return buyList.find({}).fetch();
},
'myTopViews' : function () {
return buyList.find({}).fetch();
}
});
And now the template in code:
<template name="MerchantLandingPage">
##### *** Top three trending items *** ########
{{#each top3Trending}}
ItemName:: <b>{{itemName}}</b> <br>
Item Category:: <b>{{itemCategory}}</b> <br>
Discription:: <b>{{descriptions}}</b> <br>
Image:: {{this.photo._id}} <br>
Date Created:: {{createdDate}} <br>
{{/each}}
<br><br>
############ *** My top Views *** #############
{{#each myTopViews}}
ItemName:: <b>{{itemName}}</b> <br>
Item Category:: <b>{{itemCategory}}</b> <br>
Discription:: <b>{{descriptions}}</b> <br>
Image:: {{this.photo._id}} <br>
Date Created:: {{createdDate}} <br>
{{/each}}
</template>
Both {{#each myTopViews}} and {{#each top3Trending}} successfully display but not correctly. When the variable categoryMan in
Meteor.subscribe('MerchantTrending', categoryMan, 'merchantTopViews')
changes value, it affects both both the outcome of both {{#each myTopViews}} and {{#each top3Trending}}, when its only supposed to affect {{#each top3Trending}}.
How can I get the subscriptions to NOT have an affect on both {{#each myTopViews}} and {{#each top3Trending}}, but only {{#each myTopViews}} in my template?
Thanks for the help!
Welcome to Meteor!
The solution is straight forward once you understand that:
Subscription is just a stream of your DB documents from server into your client's MiniMongoDB. So your 2 subscriptions (it is perfectly fine having several subs on the same Collection) just fill in your client's buyList local collection.
Use of Collections client side is generally independent from how you subscribe the data. So you should simply use a similar selector and possibly options in your top3Trending and myTopViews helpers as you have done for your publication server side (not the same between the 2 helpers, obviously).
As a side note, you do not even need to fetch() the Collection cursor returned by find(), Blaze knows how to handle it directly.
I see a few problems with your code, first of all - your second subscription isn't going to work because your query is wrong:
Meteor.publish('myTopViews', function (){
var currentUser = this.userId;
return buyList.find(
{ ownerId:currentUser, newArrivalsExpiryDate : {'$lte': new Date()}},
{skip: 0, limit: 3}
);
});
You had ownerId: currentUser wrapped in curly braces, it is fixed above.
The way publications/subscriptions work is, if you have two publications sending different data, the template doesn't 'know' the data is coming from two different subscriptions. It will just have access to all of the data being sent by all subscriptions.
For that reason, your two helpers top3trending and myTopViews are returning exactly the same thing. You can delete one of them!
You should move your subscriptions out of the router and in to the Template itself. Here's an article that will help you with that!
There is a package percolate:find-from-publication that permits to filter the data from publications.

Show specific menu only on one page

I've navigation menu and I would like to display the helper menu button only when the user is on specific page and be hided on others.
I've tried this way, but that doest worked
{{#if Store}}
Filters
{{/if}}
Can you please suggest how achieve this function?
in your template helper, you can lookup the route name with
Router.current().route.getName()
then you can set a helper variable to lookup if you are on this page.
Template.mytemplate.helpers({
'Store': function() {
return Router.current().route.getName() == 'Store'; //the Route name
}
});
then use the Store variable in your template as you did.

Displaying dynamic content in Meteor using Dynamic Templates

I've read through the (somewhat sparse) documentation on Dynamic Templates but am still having trouble displaying dynamic content on a user dashboard based on a particular field.
My Meteor.users collection includes a status field and I want to return different content based on this status.
So, for example , if the user has a status of ‘current’, they would see the 'currentUser' template.
I’ve been using a dynamic template helper (but have also considered using template helper arguments which may still be the way to go) but it isn’t showing a different template for users with different statuses.
{{> Template.dynamic template=userStatus}}
And the helper returns a string to align with the required template as required
userStatus: function () {
if (Meteor.users.find({_id:Meteor.userId(), status: 'active'})){
return 'isCurrent'
}
else if (Meteor.users.find({_id:Meteor.userId(), status: ‘isIdle'})) {
return 'isIdle'
} else {
return ‘genericContent'
}
}
There may be much better ways to go about this but it seems a pretty common use case.
The few examples I've seen use Sessions or a click event but I’d rather use the cursor if possible. Does this mean what I’m missing is the re-computation to make it properly reactive? Or something else incredibly obvious that I’ve overlooked.
There is a shortcut for getting the current user object, Meteor.user(). I suggest you get this object and then check the value of the status.
userStatus: function () {
if(Meteor.user()) {
if (Meteor.user().status === 'active') {
return 'currentUserTemplate'; // this should be the template name
} else if (Meteor.user().status === 'isIdle') {
return 'idleUserTemplate'; // this should be the template name
}
} else {
return ‘notLoggedInTemplate'; // this should be the template name
}
}
Ended up using this approach discussed on the Meteor forums which seems a bit cleaner.
{{> Template.dynamic template=getTemplateName}}
And the helper then becomes:
getTemplateName: function() {
return "statusTemplate" + Meteor.user().status;
},
Which means you can then use template names based on the status:
<template name="statusTemplateActive">
Content for active users
</template>
(though keep in mind that Template helpers don't like hyphens and the data context needs to be set correctly)

How to include top comment per each post as a separate collection in meteor?

If I have a collection of posts when entering a view of a posts collection, and each of these posts has a collection of comments on them, how could I list the top comment for each post along side of them?
E.g:
this.route('postsList', {
path: '/:posts',
waitOn: function() {
return Meteor.subscribe('posts');
},
data: function() {
return Posts.find({});
}
});
And then I'm iterating through the collection of posts on a page.
{{#each posts}}
{{> postItem}}
{{/each}}
<template name="postItem">
{{title}}
{{topComment}}
</template>
I'd like to put the top comment for each post item.
How can I do this with my templates/subscriptions/publications?
Posts and comments are separate collections.
If it were an embedded collection I could see the ease of use but how to deal with separate collections?
If I published a recent comment type of publication how could I subscribe to it for each post as the most recent one? Or am I thinking the wrong way here?
If you insist on having two totally separated collections, you would get into problems with efficient database queries. What you could do is to have something like recentComment field in your posts collection. Should this field point to id of the most recent comment related to the given post, you could alter your posts subscription to include the recent comments as well:
Meteor.publish('posts', function() {
var listOfIds = _.pluck(Posts.find({}, {fields: recentComment}).fetch(), 'recentComment');
return [
Posts.find(),
Comments.find({_id:{$in:listOfIds}})
];
});
Note that this solution is not fully reactive but it's good enough in most cases.

Meteor: how do you display a collection in multiple tables, where each table is a group of objects that have the same key value?

Say you have a bunch of blog posts, and each post has a "title" and "category". How would you render all post titles on a single page, where there is a table for each group of posts that have the same "category" values?
I'm starting by sorting by category, so the posts in the same category are grouped together in the cursor:
Template.postLists.posts = function() {
return Posts.find({}, {sort:{category:1}});
}
But I'm struggling with iterating through this list in a template via {{#each}}, and using Handlebars to detect when I reach a new "category", so that I can start a new , and then end the when I'm at the end of the category.
Am I coming at this the wrong way, or is there an easy way to do this?
The solution I went with was that in my Template handler, instead of returning a cursor using Posts.find(), I created a JSON object that has a structure that can be processed by a handlebars template (an array of category objects, where each category has an array of posts):
Template.postLists.categorizedPosts = function() {
var allPosts = Posts.find({}, {sort:{category:1}}).fetch();
// Then I iterate over allPosts with a loop,
// creating a new array of this structure:
// for ...
var catPosts = [ { category:"cat1", posts: [ {post1}, {post2} ] },
{ category:"cat2", posts: [ {post3}, {post4}, {post5} ] },
// etc...
];
// end loop
return catPosts;
The the template is something like this (but with tables instead of UL, just using UL for a cleaner demo here):
{{#each categorizedPosts}}
{{category}}
<ul>
{{#each posts}}
<li>{{posts.title}}</li>
{{/each}}
</ul>
{{/each}}
Note that when you return an object like this (instead of a cursor object that Posts.find() returns), Meteor's templating engine loses the ability to intelligently detect when only one of the objects in the collection has changed, and patching the DOM instead of completely re-rendering the template. So in this case, the template is completely re-rendered even if a single Posts object is updated in the DB. That's the downside. But the upside is that it works ;)

Resources