In the example given in this doc, I’m having trouble understanding how filter and data work together.
collections:
/portfolio/:
permalink: /work/{slug}/
template: work
filter: primary_tag:work
data: tag.work
Isn’t filter providing all posts with primary_tag:work to work.hbs?
But at the same time, data is also providing all posts with work tag.
Aren’t they both doing the same things?
It’s been a while since I’ve done routing, but if memory serves
Filter - tells Ghost what posts belong to this collection. Also, the post data will be available in the {{#post}} context (I think) in the work.hbs template
Data - additional data to be fetched when rendering - in the case of tag.work, the {{#tag}} context will contain the work tag data
The info from the page specified in data: is available - i.e. the feature_image comes from that page. As you used tag.work you have to use the {{#tag}} helper - if you chose a page or post you would use the {{#post}} helper.
I think this data model is poorly structured and causing confusions. Proposed solution at the bottom.
Here’s what I’ve figured out after a full day of research.
A dynamic page needs a template rather than a post or a page which are typically static (though they too can be made dynamic)
A template needs
a. title
b. header image
c. excerpt
d. posts to display
So this may make sense
collections:
/portfolio/:
permalink: /work/{slug}/
template: work
title: My title
image: header.png
excerpt: This is the summary of the resulting page
posts_filter: primary_tag:work
posts_count: 10
title, image, excerpt, posts_filter, and posts_count belong undertemplate because they are consumed by template. If they’re on the same level as template, it signifies that those fields are consumed by their parent object /portfolio/ which is a not the case.
An equivalent model using a variable would be
collections:
/portfolio/:
permalink: /work/{slug}/
template: work
data: portfolio_variables
data:
portfolio_variables:
title: My title
image: header.png
excerpt: This is the summary of the resulting page
posts_filter: primary_tag:work
posts_count: 10
A disadvantage of this model is that the template data is stored in routes.yaml which isn’t easy to modify. Where should the template data be stored that’s easy to modify?
Ghost decided to repurpose post, page, and tag objects as a storage for holding the template data (!!!)
Until now, a post or a page was an object designed to be displayed on a screen. This is no longer the case. Now, we have a variant of post or a page that’s designed to hold some values for a template, essentially a hashmap seen at the bottom of 4.
Attempting to display these new variants of post and pages will result in an incomplete page, meaning now we have posts and pages that are not suitable to be displayed on a screen.
collections:
/portfolio/:
permalink: /work/{slug}/
template: work
data: post.slug (a new variant of post that holds the template data)
In addition, Posts, Pages, and Tags tabs now show the corresponding objects (posts, pages, and tags) AND unrelated objects template data storage.
What’s worse, the existing post, page, and tag objects do not have fields for storing posts_filter or posts_count information, which are needed in a template. As a result, now we specify the data which points to a new variant of post, page, or a tagAND posts filter AND posts count.
collections:
/portfolio/:
permalink: /work/{slug}/
template: work
data: post.slug (a new variant of post that holds the template data, except for the filter and limit)
filter: primary_tag:work
limit: 10
At this point, the keyword data doesn’t make sense anymore. If data holds “the data used by the template”, why aren’t filter and limit included in the data? (Who even named this field data? That’s just a generic word. static_fields would be more accurate.)
And to make it even more confusing, data, filter, and limit are on the same level as template, arriving at
Your analysis appears to use Template when, perhaps you mean Index (aka Archive in some places in Ghost).
For me:
Template - a Handlebar template .hbs - used to render a Route Index, a Collection Index, a single Post, a Tag Index etc.
Index - a page that fronts a set of [Post|Page|Tag|Author] - needs a Template to render
an Index needs it’s own attributes as well as a Paginated set of children
Route - a set of Articles, fronted by a paginated Index.
Articles can appear in many Routes
Collection - a set of Articles, fronted by a paginated Index.
Collections are a structural / semantic sub-division of Articles
Articles only appear in ONE Collection
The Filter on a Collection is a first-order attribute - it defines the Collection
So - I actually prefer the way is currently is because (IMHO):
KISS - current approach is simple to understand and use.
it fits the style/approach/philosophy/tools used to write Ghost - it’s important that Ghost has an internally consistent approach.
Collections are a Very-Special-Case - used to create structural semantic divisions in Articles. I think this is why the rules are simple (and hence restrictive).
You have moved the Collection-Filter into Data, but:
Filter defines the Collection - it is a material first-order attribute of the Collection - it’s not data to be tweaked, like Title, Accent Colour etc.
Permalink is also structural - remember that song? Don’t go breaking my Links
If you want to use Filter in a Template - you should probably be using a Route.
Safety - I want to be able to change the Title, Image etc. safely using the Admin editor
this limits the scope of the change
routes.yaml is sensitive and affects the entire Site.
Much safer for users of a custom Theme
My main bugbear with the setup is the inconsistency between the Admin editor and the model - for example:
create a Template called custom-authors.hbs
create a static Page called /authors/ - and select the custom-authors.hbs as the template
on the /authors/ Page set the Title → you can now render this {{title}} in your Template.
on the /authors/ Page add some Content - you might think that you can use {{content}} - the Content is actually in {{excerpt}} - meh… this is just something you have to discover.
Thank you for the detailed response. I really appreciate it.
Index does sound like what I’m looking for. I saw the doc for it but I don’t understand the “Contexts & Templates” section. It talks about how to detect it, but how do I have a custom URL to be an index? For example, how do I set /archive/ to use Index context?
I can see that logic for collections because as you noted collections are a Very-Special-Case.
But how about the same syntax used in routes? Deciding the filter or limit currently requires editing routes.yaml.
routes.yaml should be about routing. I think it should simply point to a resource which has more information, rather than have content information. Imagine our wifi routers caring about the content - doesn’t seem right.
I agree and this is why I think it’s bad to repurpose post, page, and tag as a template data storage.
It breaks the paradigm that post and page should be displayed on the screen, as now they must also act as a template data storage. Ghost haphazardly patches this issue by redirecting the slug of the data source to the route, so that the undisplayable post and page can’t be displayed.
Because post, page, tag aren’t designed to be a template data storage, it doesn’t have a place to store filter and limit which causes the fields to spill over into routes.yaml
you might think that you can use {{content}} - the Content is actually in {{excerpt}} IMO this is again exactly because page is reused to be a template data storage. It’s not made to be one, and now it has to act as one, therefore the abstraction becomes inconsistent.
I see Posts, Pages, Tags as first-order information schema, rather than as mere holding places for Template display.
I see the Templates as secondary helpers that transform the structured information into a suitable format for a particular rendering surface - HTML, Print-language, RSS feed etc.
I view Routes as a convenient, permanent search of Articles, for Consumers.
I view Collections as fundamental structural sub-divisions of Articles.
That’s why I quite like the information being stored in Pages, Posts, Tags - my niggles are all small trivial implementation details.
The Context stuff is all the juicy goodness that the Ghost developers have given us to make life much easier in Template land - think of them as straight-forward interfaces to the structured data in your Articles (Posts) and using a consistent approach, also to the extra structured data you need to implement a site - Pages and Tags.
The easiest way to grasp it is to play with it all e.g. a series of small features like:
What I don’t understand is how to apply Index context to /linux/. I understand how to use {{#get}} to query the posts and show it. However, on an Index context, pagination is baked into the URL /linux/:num/ based on the data from package.json.
Given a page with a list using index.hbs template, how do I use Index context on it?