{"_id":"5937b2398e8d9b002f1df2a2","category":{"_id":"5937b2388e8d9b002f1df274","version":"5937b2378e8d9b002f1df26e","project":"542fe92a5eceb608003fddc8","__v":0,"sync":{"url":"","isSync":false},"reference":false,"createdAt":"2015-11-04T19:48:37.185Z","from_sync":false,"order":5,"slug":"cookbook","title":"Cookbook"},"parentDoc":null,"project":"542fe92a5eceb608003fddc8","user":"542c5cfcddd3190e00228849","version":{"_id":"5937b2378e8d9b002f1df26e","project":"542fe92a5eceb608003fddc8","__v":1,"createdAt":"2017-06-07T07:58:47.936Z","releaseDate":"2017-06-07T07:58:47.936Z","categories":["5937b2388e8d9b002f1df26f","5937b2388e8d9b002f1df270","5937b2388e8d9b002f1df271","5937b2388e8d9b002f1df272","5937b2388e8d9b002f1df273","5937b2388e8d9b002f1df274"],"is_deprecated":false,"is_hidden":false,"is_beta":false,"is_stable":true,"codename":"","version_clean":"1.0.0","version":"1.0.0"},"__v":0,"updates":["56580a6f3df5130d00a579c9","567ab95c93919f0d00c979e1"],"next":{"pages":[],"description":""},"createdAt":"2015-11-04T22:03:00.656Z","link_external":false,"link_url":"","githubsync":"","sync_unique":"","hidden":false,"api":{"results":{"codes":[]},"settings":"","auth":"required","params":[],"url":""},"isReference":false,"order":2,"body":"[block:callout]\n{\n  \"type\": \"warning\",\n  \"title\": \"Beta Feature\",\n  \"body\": \"This recipe uses the `{{#get}}` helper, which is currently a Beta feature and should not be used in production. For more information on what Beta means, see [here](https://help.ghost.org/hc/en-us/articles/115000301672-Public-API-Beta).\\n\\nTo enable this feature, visit yourblog.com/ghost/#/settings/labs/ and tick the 'Public API' checkbox.\"\n}\n[/block]\nEveryone loves sidebars: the blogger's one-stop-shop for their latest tweets, email lists, tag clouds, and so much more.\n\nIn this recipe, we'll walk through setting up a sidebar, using the [{{#get}} helper](doc:get) to fetch some posts from our blog, and then make sure we don't output the same post we're already looking at.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"1. Creating a sidebar\"\n}\n[/block]\nThis recipe is all about Ghost theming with handlebars, so I'm going to assume you can write HTML & CSS to layout your sidebar how you want.\n\nIdeally, we want to use the same sidebar partial on every single page. To do that, we're going to setup our sidebar to be called from a partial, inside of `default.hbs`:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"...\\n{{ghost_head}}\\n</head>\\n<body class=\\\"{{body_class}}\\\">\\n  <section class=\\\"blog-content\\\">\\n    {{{body}}}\\n  </section>\\n\\n  {{> sidebar}}\\n  \\n  {{ghost_foot}}\\n</body>\",\n      \"language\": \"handlebars\"\n    }\n  ]\n}\n[/block]\nThis is a simplified version of a `default.hbs` template showing the key parts needed to get this recipe to work. \n\nNow we need our sidebar partial, `partials/sidebar.hbs` to also have some basic HTML:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<aside class=\\\"blog-sidebar\\\">\\n  <section class=\\\"recent-posts\\\">\\n    <h3>Recent posts</h3>\\n    <ol>\\n      <li><a href=\\\"\\\"></a></li>\\n    </ol>\\n  </section>\\n  \\n  <section class=\\\"featured-posts\\\">\\n    <h3>Featured posts</h3>\\n    <ol>\\n      <li><a href=\\\"\\\"></a></li>\\n    </ol>\\n  </section>\\n</aside>\",\n      \"language\": \"html\"\n    }\n  ]\n}\n[/block]\nWith our templates setup, it's time to look at how we can list out our posts\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"2. Fetching data\"\n}\n[/block]\nBy default, Ghost only provides the bare essential data to build your theme. The data available on each 'page' is listed out in the [context table](/docs/context-overview#context-table). In this case, we need some extra data. \n\nTo do this, we're going to use the `{{#get}}` helper. Our data will stay the same unless the blog is updated, meaning it counts as 'static' data. If we wanted to fetch dynamic data - for example random posts - which changes without the blog being updated, then we would need to use [ajax](doc:using-the-ghost-api-via-ajax) instead.\n\nThe `{{#get}}` helper will fetch us any public data we want. To start with, we want a list of the most recent 3 posts published on the blog. So we want to fetch \"posts\" and we want to add a limit of 3. \n\nThe call to the get helper will need to look like `{{#get \"posts\" limit=\"3\"}}`, we want to call these our \"recent\" posts, so we'll also use the block parameter syntax to name the data: `{{#get \"posts\" limit=\"3\" as |recents|}}`. Once we've fetched our data, we'll need to use the [{{foreach}}](doc:foreach) helper to iterate over the posts.\n\nPlugging this into our sidebar, our recent posts block will look something like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"{{#get \\\"posts\\\" limit=\\\"3\\\" as |recents|}}\\n<section class=\\\"recent-posts\\\">\\n  <h3>Recent posts</h3>\\n  <ol>\\n    {{#foreach recents}}\\n    <li><a href=\\\"{{url}}\\\">{{title}}</a></li>\\n    {{/foreach}}\\n  </ol>\\n</section>\\n{{/get}}\",\n      \"language\": \"handlebars\"\n    }\n  ]\n}\n[/block]\nNotice how we put the `{{#get}}` helper call outside of all of the HTML. This means that if no posts are found, we won't get an empty `<section>`. You can use an `{{else}}` block with the `{{#get}}` helper to output something else if the request returns no results.\n\nGetting recent posts is fairly easy - the get helper makes a call to the \"posts\" endpoint of the API, and this returns posts in the order of most recently published first by default, as that's what a blog does :wink: We only have to set the 'limit' parameter to make sure we don't get too many (the default is 15, which is a lot for a sidebar).\n\nTo fetch our featured posts, we have to ask the `{{#get}}` helper to only return posts which are marked as `featured`, using a filter parameter. In this case, the query we want to do is `filter=\"featured:true\"`. We also still only want 3 posts, so our call to the get helper will look like `{{#get \"posts\" filter=\"featured:true\" limit=\"3\" as |featured|}}`. It doesn't matter what order you add the parameters like `limit` and `filter`, but the resource must come first (\"posts\") and the `as |name|` block parameters have to come last.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"{{#get \\\"posts\\\" filter=\\\"featured:true\\\" limit=\\\"3\\\" as |featured|}}\\n<section class=\\\"featured-posts\\\">\\n  <h3>Featured posts</h3>\\n  <ol>\\n    {{#foreach featured}}\\n    <li><a href=\\\"{{url}}\\\">{{title}}</a></li>\\n    {{/foreach}}\\n  </ol>\\n</section>\\n{{else}}\\n<p>No featured posts...</p>\\n{{/get}}\",\n      \"language\": \"handlebars\"\n    }\n  ]\n}\n[/block]\nPlugging this into our HTML looks much the same as our recent posts list, only this time I've added an `{{else}}` block just to demonstrate how this would be done.\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"3. Context awareness\"\n}\n[/block]\nThese two lists will now output 3 posts. The first will output the most recent posts, and the second will output featured posts. This will appear on every page of the blog, regardless of whether you're on the home page or reading a particular post.\n\nThe idea behind a sidebar is usually to show users some other content they might like to read, so if you're currently looking at a particular post, you might not want the sidebar to output that same post.\n\nTaking our featured posts example a little further, let's change it so that it never shows any posts which are already shown on the page. \n\nThe first thing we're going to do, is move our HTML for outputting a section containing a list of posts into its own partial, so that we don't have to duplicate this code later. The new partial is called `sidebar-posts`. We can pass our partials some custom data, so we tell it what we want our title to be, and pass the `featured` posts renaming them to `sidebarPosts`. Inside the sidebar-posts partial we output our title and our posts.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"{{#get \\\"posts\\\" filter=\\\"featured:true\\\" limit=\\\"3\\\" as |featured|}}\\n{{> \\\"sidebar-posts\\\" sidebarTitle=\\\"Featured Posts\\\" sidebarPosts=featured}}\\n{{/get}}\",\n      \"language\": \"handlebars\",\n      \"name\": \"partials/sidebar.hbs\"\n    }\n  ]\n}\n[/block]\nOur partial uses the data that was passed to it:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<section class=\\\"sidebar-posts\\\">\\n  <h3>{{sidebarTitle}}</h3>\\n  <ol>\\n    {{#foreach sidebarPosts}}\\n    <li><a href=\\\"{{url}}\\\">{{title}}</a></li>\\n    {{/foreach}}\\n  </ol>\\n</section>\",\n      \"language\": \"handlebars\",\n      \"name\": \"partials/sidebar-posts.hbs\"\n    }\n  ]\n}\n[/block]\nNow that our `sidebar.hbs` is nice and lean, we can start adding code to make sure we don't display the posts already listed.\n\nWe're going to use the `{{#is}}` helper to check if we're currently on a post or a page: `{{#is \"post, page\"}}`. If we are on a post or a page, we have a single post that we don't want to include. If we are currently viewing a whole list, then we have multiple posts to exclude - the code for this will be different.\n\nFirst, to filter out a single post we'll need to exclude `{{post.id}}` in our filter parameter. The code for our `sidebar.hbs` should be looking something like this:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"{{#is \\\"post, page\\\"}}\\n  {{#get \\\"posts\\\" limit=\\\"3\\\" filter=\\\"featured:true+id:-{{post.id}}\\\"}}\\n    {{> \\\"sidebar-posts\\\" sidebarTitle=\\\"Featured Posts\\\" sidebarPosts=posts}}\\n  {{/get}}\\n{{/is}}\",\n      \"language\": \"handlebars\"\n    }\n  ]\n}\n[/block]\nSo, our filter query now reads \"featured is true and id is not the current post id\". The `+` means 'and' and the `-` means 'not'. As we're querying posts, the `id` refers to the post id.\n\nNext, lets do a similar query for if we're not on a post or a page. In this case, we need to exclude all of the post ids that are already being output. To do this, we don't just need `-` or not, but we need the `-[...]` syntax that means 'not in'. \n\nWe can use the syntax `{{posts[*].id}}` to get a list of all the post ids that are already present in the data, so that we end up with the query `id:-[{{posts[*].id}}]`. The full query reads \"featured is true and id is not in the current set of post ids\". \n\nIt's a little fiddly to put together but hopefully this demonstrates the full power of what you can do with a filter query and the `{{#get}}` helper.\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"<aside class=\\\"blog-sidebar\\\">\\n{{#is \\\"post, page\\\"}}\\n  {{#get \\\"posts\\\" limit=\\\"3\\\" filter=\\\"featured:true+id:-{{post.id}}\\\"}}\\n    {{> \\\"sidebar-posts\\\" sidebarTitle=\\\"Featured Posts\\\" sidebarPosts=posts}}\\n  {{/get}}\\n{{else}}\\n  {{#get \\\"posts\\\" limit=\\\"3\\\" filter=\\\"featured:true+id:-[{{posts[*].id}}]\\\"}}\\n    {{> \\\"sidebar-posts\\\" sidebarTitle=\\\"Featured Posts\\\" sidebarPosts=posts}}\\n  {{/get}}\\n{{/is}}\\n</aside>\",\n      \"language\": \"handlebars\"\n    }\n  ]\n}\n[/block]\n\n[block:api-header]\n{\n  \"type\": \"basic\",\n  \"title\": \"Extra special sauce: related posts\"\n}\n[/block]\nIf you'd like to show related posts, you can modify the get helper query to do this based on tags:\n[block:code]\n{\n  \"codes\": [\n    {\n      \"code\": \"{{#get \\\"posts\\\" limit=\\\"5\\\" filter=\\\"tags:[{{post.tags}}]+id:-{{post.id}}\\\" include=\\\"tags\\\"}}\\n{{/get}}\",\n      \"language\": \"handlebars\"\n    }\n  ]\n}\n[/block]","excerpt":"Use the get helper to build a sidebar","slug":"recent-featured-sidebar","type":"basic","title":"Get Helper: Recent or featured posts sidebar"}

Get Helper: Recent or featured posts sidebar

Use the get helper to build a sidebar

[block:callout] { "type": "warning", "title": "Beta Feature", "body": "This recipe uses the `{{#get}}` helper, which is currently a Beta feature and should not be used in production. For more information on what Beta means, see [here](https://help.ghost.org/hc/en-us/articles/115000301672-Public-API-Beta).\n\nTo enable this feature, visit yourblog.com/ghost/#/settings/labs/ and tick the 'Public API' checkbox." } [/block] Everyone loves sidebars: the blogger's one-stop-shop for their latest tweets, email lists, tag clouds, and so much more. In this recipe, we'll walk through setting up a sidebar, using the [{{#get}} helper](doc:get) to fetch some posts from our blog, and then make sure we don't output the same post we're already looking at. [block:api-header] { "type": "basic", "title": "1. Creating a sidebar" } [/block] This recipe is all about Ghost theming with handlebars, so I'm going to assume you can write HTML & CSS to layout your sidebar how you want. Ideally, we want to use the same sidebar partial on every single page. To do that, we're going to setup our sidebar to be called from a partial, inside of `default.hbs`: [block:code] { "codes": [ { "code": "...\n{{ghost_head}}\n</head>\n<body class=\"{{body_class}}\">\n <section class=\"blog-content\">\n {{{body}}}\n </section>\n\n {{> sidebar}}\n \n {{ghost_foot}}\n</body>", "language": "handlebars" } ] } [/block] This is a simplified version of a `default.hbs` template showing the key parts needed to get this recipe to work. Now we need our sidebar partial, `partials/sidebar.hbs` to also have some basic HTML: [block:code] { "codes": [ { "code": "<aside class=\"blog-sidebar\">\n <section class=\"recent-posts\">\n <h3>Recent posts</h3>\n <ol>\n <li><a href=\"\"></a></li>\n </ol>\n </section>\n \n <section class=\"featured-posts\">\n <h3>Featured posts</h3>\n <ol>\n <li><a href=\"\"></a></li>\n </ol>\n </section>\n</aside>", "language": "html" } ] } [/block] With our templates setup, it's time to look at how we can list out our posts [block:api-header] { "type": "basic", "title": "2. Fetching data" } [/block] By default, Ghost only provides the bare essential data to build your theme. The data available on each 'page' is listed out in the [context table](/docs/context-overview#context-table). In this case, we need some extra data. To do this, we're going to use the `{{#get}}` helper. Our data will stay the same unless the blog is updated, meaning it counts as 'static' data. If we wanted to fetch dynamic data - for example random posts - which changes without the blog being updated, then we would need to use [ajax](doc:using-the-ghost-api-via-ajax) instead. The `{{#get}}` helper will fetch us any public data we want. To start with, we want a list of the most recent 3 posts published on the blog. So we want to fetch "posts" and we want to add a limit of 3. The call to the get helper will need to look like `{{#get "posts" limit="3"}}`, we want to call these our "recent" posts, so we'll also use the block parameter syntax to name the data: `{{#get "posts" limit="3" as |recents|}}`. Once we've fetched our data, we'll need to use the [{{foreach}}](doc:foreach) helper to iterate over the posts. Plugging this into our sidebar, our recent posts block will look something like this: [block:code] { "codes": [ { "code": "{{#get \"posts\" limit=\"3\" as |recents|}}\n<section class=\"recent-posts\">\n <h3>Recent posts</h3>\n <ol>\n {{#foreach recents}}\n <li><a href=\"{{url}}\">{{title}}</a></li>\n {{/foreach}}\n </ol>\n</section>\n{{/get}}", "language": "handlebars" } ] } [/block] Notice how we put the `{{#get}}` helper call outside of all of the HTML. This means that if no posts are found, we won't get an empty `<section>`. You can use an `{{else}}` block with the `{{#get}}` helper to output something else if the request returns no results. Getting recent posts is fairly easy - the get helper makes a call to the "posts" endpoint of the API, and this returns posts in the order of most recently published first by default, as that's what a blog does :wink: We only have to set the 'limit' parameter to make sure we don't get too many (the default is 15, which is a lot for a sidebar). To fetch our featured posts, we have to ask the `{{#get}}` helper to only return posts which are marked as `featured`, using a filter parameter. In this case, the query we want to do is `filter="featured:true"`. We also still only want 3 posts, so our call to the get helper will look like `{{#get "posts" filter="featured:true" limit="3" as |featured|}}`. It doesn't matter what order you add the parameters like `limit` and `filter`, but the resource must come first ("posts") and the `as |name|` block parameters have to come last. [block:code] { "codes": [ { "code": "{{#get \"posts\" filter=\"featured:true\" limit=\"3\" as |featured|}}\n<section class=\"featured-posts\">\n <h3>Featured posts</h3>\n <ol>\n {{#foreach featured}}\n <li><a href=\"{{url}}\">{{title}}</a></li>\n {{/foreach}}\n </ol>\n</section>\n{{else}}\n<p>No featured posts...</p>\n{{/get}}", "language": "handlebars" } ] } [/block] Plugging this into our HTML looks much the same as our recent posts list, only this time I've added an `{{else}}` block just to demonstrate how this would be done. [block:api-header] { "type": "basic", "title": "3. Context awareness" } [/block] These two lists will now output 3 posts. The first will output the most recent posts, and the second will output featured posts. This will appear on every page of the blog, regardless of whether you're on the home page or reading a particular post. The idea behind a sidebar is usually to show users some other content they might like to read, so if you're currently looking at a particular post, you might not want the sidebar to output that same post. Taking our featured posts example a little further, let's change it so that it never shows any posts which are already shown on the page. The first thing we're going to do, is move our HTML for outputting a section containing a list of posts into its own partial, so that we don't have to duplicate this code later. The new partial is called `sidebar-posts`. We can pass our partials some custom data, so we tell it what we want our title to be, and pass the `featured` posts renaming them to `sidebarPosts`. Inside the sidebar-posts partial we output our title and our posts. [block:code] { "codes": [ { "code": "{{#get \"posts\" filter=\"featured:true\" limit=\"3\" as |featured|}}\n{{> \"sidebar-posts\" sidebarTitle=\"Featured Posts\" sidebarPosts=featured}}\n{{/get}}", "language": "handlebars", "name": "partials/sidebar.hbs" } ] } [/block] Our partial uses the data that was passed to it: [block:code] { "codes": [ { "code": "<section class=\"sidebar-posts\">\n <h3>{{sidebarTitle}}</h3>\n <ol>\n {{#foreach sidebarPosts}}\n <li><a href=\"{{url}}\">{{title}}</a></li>\n {{/foreach}}\n </ol>\n</section>", "language": "handlebars", "name": "partials/sidebar-posts.hbs" } ] } [/block] Now that our `sidebar.hbs` is nice and lean, we can start adding code to make sure we don't display the posts already listed. We're going to use the `{{#is}}` helper to check if we're currently on a post or a page: `{{#is "post, page"}}`. If we are on a post or a page, we have a single post that we don't want to include. If we are currently viewing a whole list, then we have multiple posts to exclude - the code for this will be different. First, to filter out a single post we'll need to exclude `{{post.id}}` in our filter parameter. The code for our `sidebar.hbs` should be looking something like this: [block:code] { "codes": [ { "code": "{{#is \"post, page\"}}\n {{#get \"posts\" limit=\"3\" filter=\"featured:true+id:-{{post.id}}\"}}\n {{> \"sidebar-posts\" sidebarTitle=\"Featured Posts\" sidebarPosts=posts}}\n {{/get}}\n{{/is}}", "language": "handlebars" } ] } [/block] So, our filter query now reads "featured is true and id is not the current post id". The `+` means 'and' and the `-` means 'not'. As we're querying posts, the `id` refers to the post id. Next, lets do a similar query for if we're not on a post or a page. In this case, we need to exclude all of the post ids that are already being output. To do this, we don't just need `-` or not, but we need the `-[...]` syntax that means 'not in'. We can use the syntax `{{posts[*].id}}` to get a list of all the post ids that are already present in the data, so that we end up with the query `id:-[{{posts[*].id}}]`. The full query reads "featured is true and id is not in the current set of post ids". It's a little fiddly to put together but hopefully this demonstrates the full power of what you can do with a filter query and the `{{#get}}` helper. [block:code] { "codes": [ { "code": "<aside class=\"blog-sidebar\">\n{{#is \"post, page\"}}\n {{#get \"posts\" limit=\"3\" filter=\"featured:true+id:-{{post.id}}\"}}\n {{> \"sidebar-posts\" sidebarTitle=\"Featured Posts\" sidebarPosts=posts}}\n {{/get}}\n{{else}}\n {{#get \"posts\" limit=\"3\" filter=\"featured:true+id:-[{{posts[*].id}}]\"}}\n {{> \"sidebar-posts\" sidebarTitle=\"Featured Posts\" sidebarPosts=posts}}\n {{/get}}\n{{/is}}\n</aside>", "language": "handlebars" } ] } [/block] [block:api-header] { "type": "basic", "title": "Extra special sauce: related posts" } [/block] If you'd like to show related posts, you can modify the get helper query to do this based on tags: [block:code] { "codes": [ { "code": "{{#get \"posts\" limit=\"5\" filter=\"tags:[{{post.tags}}]+id:-{{post.id}}\" include=\"tags\"}}\n{{/get}}", "language": "handlebars" } ] } [/block]