-
Notifications
You must be signed in to change notification settings - Fork 41
How to render Apostrophe areas your own way, or access their content directly
The usual way to render an Apostrophe area is this:
{{ aposArea({ slug: page.slug + ':body', area: page.areas.body, edit: edit }) }}
This is great. It renders all the widgets (like slideshows) and all the rich text for you and you really don't have to think about it...
Except when you do have to think about it, because your needs are different. This HOWTO is dedicated to those special cases.
In many cases what you want is supported by options to the widgets inside your area. You can pass options to those widgets when calling aposArea.
Here's an aposArea call that sets the noHeight
option on the slideshow widget. You just pass the options intended for slideshows as the "slideshow" option to the area. You can do this for any type of widget:
{{ aposArea({ slug: slug + ":body", area: page.areas.body, edit: edit,
slideshow:{ noHeight: true } }) }}
The noHeight option is already built in. But if it wasn't, you could override apostrophe/views/slideshow.html
by copying it to views/global/slideshow.html
in your project, and refer to options.noHeight in your own template code.
In general, if your complaint is that you don't like the markup for a widget, and CSS is not enough to fix it, you should override the template for the widget.
For example, copy apostrophe/views/slideshow.html
to views/global/slideshow.html
in your project. Now you can adjust the markup any way you see fit. Just keep in mind there's a right way to add JavaScript that enhances your markup as explained below.
If your complaint about slideshows (or some other widget) is the way our JavaScript works in the browser, you can easily change it in your project's site.js
file.
Every widget is enhanced by a simple player function, like this one:
apos.widgetPlayers.slideshow = function($el)
{
// Use our jQuery slideshow plugin
var data = apos.getWidgetData($el);
$el.projector({
noHeight: data.noHeight,
delay: data.delay
});
};
Here we look at the options of the widget and fire up our jQuery Projector plugin.
Do you hate jQuery Projector? No problem. Just set apos.widgetPlayers.slideshow
to a different function of your own in site.js
. Boom!
Apostrophe will automatically call this method once for every widget that has the "slideshow" type.
$el
will be a jQuery object containing the widget you need to enhance.
apos.getWidgetData($el)
fetches all the data for the widget. For slideshows that includes any options you passed to it and also the ._items
array. Of course you can also find your images via jQuery calls like $el.find('[data-image]')
.
Players are called once for each widget. Please do not fight this by trying to enhance all the widgets at once. Just pay attention to the widget in $el. You will get another call for each widget.
If you heed this rule, you will not get double-bound event handlers, and everything will just work when Apostrophe needs to refresh only part of the page.
This is the most common reason why people want to access area content directly.
You can use aposAreaFindFile({ area: area, group: 'images' })
to just get the first file object in an area. This function searches through slideshows, marquees, etc. in the area until it finds an image.
You can skip the group
option if you know the area only contains images and never PDFs, etc.
You can also specify group: 'office'
if you are only interested in PDFs and DOCX files and so on. Or you can specify an extension with extension: 'jpg'
or extension: 'pdf'
or even extensions: [ 'pdf', 'docx' ]
.
You should always use aposFilePath(file, { size: 'one-sixth' })
to get from a file object to a URL. The size option can be any of the sizes you set up in your project. The default size names are full, two-thirds, one-half, one-third and one-sixth. If you want the original size, you can skip the second argument, but remember it could be a humungous 7MB file.
aposAreaFindFile
is fast, so it is OK to use it in an if
statement and then use it again inside an img
element, like in this complete example:
{% if aposAreaFindFile({ area: item.areas.thumbnail }) %}
<img src="{{ aposFilePath(aposAreaFindFile({ area: item.areas.thumbnail }), { size: 'one-third' }) }}" />
{% endif %}
You can fetch the plaintext of an area with aposAreaPlaintext. This method ignores images, etc. and just returns text suitable for use in a summary, with no rich text markup. Make sure you escape it with "e" if you output it with nunjucks:
{{ aposAreaPlaintext({ area: snippet.body, truncate: 200 }) | e }}
Here I use the truncate option to cut the text short close to 200 characters (but without breaking a word, so it will be a little shorter).
aposAreaFindFile
and aposAreaPlaintext
can be used together to implement lightweight summaries of blog posts and similar objects.
If you truly are not seeing what you need above, it's not hard to access the content of an area directly.
Here's how it works:
If I have an area object, then area.items
is an array of its content items.
Each item in that array has a "type" property which tells you what kind of item it is.
If item.type
is richText, then item.content
contains the rich text markup. All the other possible types are widgets, like "slideshow" or "buttons".
If item
is a slideshow widget, buttons widget, marquee widget or files widget, then item._items
will be an array of its files.
If item
is a video widget, then item.video
is the video URL. item.thumbnail
is the thumbnail image URL.
In general, and very much on purpose, the structure of area and page objects in A2 is vastly simpler than in A1.5 and there's nothing scary about working with them.
We intend it to stay simple, but it may change a little as A2 matures. So if you bypass all the other methods above and go straight for the data, be sure to follow the apostrophenow Google Group. And while you're at it, write a message to the group describing your use case and why the built-in features didn't cover it. That may help us design future APIs.