Skip to content

How to render Apostrophe areas your own way, or access their content directly

Tom Boutell edited this page Aug 17, 2013 · 3 revisions

The basics: plain old aposArea

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.

Passing options to widgets in areas

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 } }) }}

Adding your own options and overriding widget templates

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.

"What about the slideshow javascript?" Customizing widget players

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.

Fetching one image from an area

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 %}

Fetching the plaintext of an area

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.

Accessing the content of an item directly

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.