Collections, Blocks & Spreadsheets

Beyond the 5 standard Mustache tags we have 3 more unique tags that are available to us. These tags enable us to create repeatable types of content, like sessions in an agenda, sections of a website or an array of name badges from an attendee list.

Collection

Tag Declarations

Before we dive in, let’s look at the tags that we will be using. For all three of these repeating variables we use both an opening and closing tag to wrap markup. All are double braced and use the # operator in the opening tag. To close off these tags we can use a slash (/). After the opening or closing tags we then declare what the tag is (ie. is it a collection, spreadsheet or block?). Finally after that declaration we can give that tag a name, this name will be the assigned Outfit input name. So here’s an example of each.

{{ #collection.agenda }}

  -- INNER CONTENT --

{{ /collection.agenda }}


{{ #block.text-section }}

  -- INNER CONTENT --

{{ /block.text-section }}


{{ #spreadsheet.name-badges }}

  -- INNER CONTENT --

{{ /spreadsheet.name-badges }}

What is a collection?

A collection is our most basic repeating tag. A collection works by wrapping HTML/CSS/Mustache markup in {{ collection }} tags. Whenever an author creates a new collection, when editing a document, the wrapped content is copied and repeated, except for the inputs that are defined within the collection. These can have unique values per each collection item.

As an example imagine we have a list of Speakers for an event, we can turn that into a Collection with a few localised inputs.

{{ #collection.speakers }}
  <div class="speaker-card" style="background-image: url('{{{ background-image }}}')">
    <h1>{{{ name }}}</h1>
    <h2>{{{ position }}}</h2>
    <p>{{{ bio }}}</p>
  </div>
{{ /collection.speakers }}

So when we create a collection and it’s local inputs, we first create the collection and then (if the inputs are new to the account) we define the localised inputs. We can also set a maximum amount of collection items. This is extremely handy when a template only has a defined space for a collection and we need to restrict authors on the amount of items they can render in a document. By default there is no maximum item length.

max items for a collection

On the Author side, when we create a collection item, we clone the initial markup and populate the localised inputs with appropriate values. We repeat the process to create more items in a collection (in this example, we’d repeat the process to add more speakers).

gif of using a collection

The data that we just created would look something like this if we were to dig around the HTML. Notice that the wrapped markup from above is the exact same, however the values of the Mustache is unique to each cloned speaker-card.

<div class="speaker-card" style="background-image: url('james.jpg')">
  <h1>James Lee</h1>
  <h2>Designer</h2>
  <p>James is a passionate guy, who really likes his coffee.</p>
</div>

<div class="speaker-card" style="background-image: url('john.jpg')">
  <h1>John Morris</h1>
  <h2>Designer</h2>
  <p>John also a passionate person, with many passions</p>
</div>

<div class="speaker-card" style="background-image: url('kayel.jpg')">
  <h1>Kayel McCraw</h1>
  <h2>Designer</h2>
  <p>Weirdly enough Kayel is also passionate about stuff.</p>
</div>

When editing, authors also have the ability to reorder collection items by clicking on a collection item, holding the click and dragging the item to its desired position.

ordering a collection

Blocks

Blocks are an optional component that must always be contained within a collection. They enable a designer to specify different types of components that can be repeated within a collection. Unlike a simple collection, that repeats the same contained markup for every new collection item, a series of blocks lets us define different markup compositions and different mustache values depending on the type of block an author wants to add into their collection.

The best way to illustrate how blocks work is to imagine the markup for an infosite. An infosite can have many sections with various section types. There could be a text section, a video section, a gallery section, a contact section and on it goes. Within Outfit we can’t simply satisfy all of these section types with one collection layout, so we can use blocks for each of those sections.

Below is an example of the block markup. Notice that the blocks must be contained within a larger collection and then within each block is the specific markup and mustache tags.

{{ #collection.infosite-sections }}
  
  {{ #block.hero-section }}
    <section id="hero" style="background-image: url('{{{ background-image }}}')">
      <h1>{{{ headline }}}</h1>
      <h2>{{{ subheading }}}</h2>
      <img src="{{{ logo }}}"/>
    </section>
  {{ /block.hero-section }}

  {{ #block.text-section }}
    <section id="text">
      <h1>{{{ text-headline }}}</h1>
      <div>{{{ text-body-copy }}}</div>
    </section>
  {{ /block.text-section }}
  
  {{ #block.contact-section }}
    <section id="contact">
      <h1>{{{ contact-headline }}}</h1>
      <p>Address: {{{ address }}}</p>
      <p>Phone: {{{ phone-number }}}</p>
      <p>Email: <a href="mailto:{{{ email }}}">{{{ email }}}</a></p>
    </section>
  {{ /block.contact-section }}
  
{{ /collection.infosite-sections }}

So when we create these inputs we now have an extra step to perform, not only do we have to create the collection and it’s inputs, but now we create the collection, the blocks that are housed within the collection and then the block specific inputs.

creating a block

Now if we were to look at the workflow of adding these blocks we click into the collection and from the drop-down we see our blocks to choose from. We can then add that block, define the inputs inside the block and repeat the process with the other block types that we have at our disposal.

editing a block element

Like we showed for a collection we also have the ability to reorder blocks, repeat the same block type and we can even utilise a maximum amount of collection items for blocks.

Spreadsheets

A spreadsheet, like a collection, allows authors to create repeatable sections of content however the values of a spreadsheet are always text based and are input within spreadsheet cells, much like you would in Excel or Google Sheets. Spreadsheets are great for long, database driven cases like the following:

  • Using a company employee list to create personal business cards
  • An attendee list to print event name badges
  • Yearly reporting to render charts and tables
  • Even smaller cases like simply printing a list of headlines to display for a newsletter.

So lets say I want to create a document full of business cards for all of my employees. The markup to do this is very similar to a collection, where markup and mustache tags are contained in a spreadsheet specific opening and closing tag.

{{ #spreadsheet.business-cards }}
  <div class="business-card">
    <h1>{{{ first-name }}}</h1>
    <h1>{{{ last-name }}}</h1>
    <h3>{{{ position }}}</h3>
    
    <p>E: {{{ email }}}</p>
    <p>P: {{{ phone }}}</p>
  </div>
{{ /spreadsheet.business-cards }}

When creating the inputs instead of having the tags automatically detected and opened to customisation, because these inputs are all the same type (text) we can go in and add these tags within the spreadsheet. The label is what will be rendered in the spreadsheet and the property is the tag name from the template. Please note that any tags found inside the spreadsheet must be accessed via the spreadsheet and cannot be given a customised input type.

creating a spreadsheet

To break down how a Spreadsheet works on the author side the equivalent of a collection item is a row on a spreadsheet. So one row in this instance equals one business card and so on. Along the columns are the editable inputs that are defined within the spreadsheet. During the editing process we populate the spreadsheet and then when we click Back we can see the various rows, populated as separate business cards.

editing a spreadsheet

When populating a spreadsheet we have the ability to do a couple of things:

  1. Firstly we can copy and paste columns and rows of data from Excel or Google Sheets, so if the data is formatted correctly we can copy that straight into Outfit.
  2. Conversely we also have the ability to upload a CSV if the column structure matches the columns in the Outfit spreadsheet.

Conditional Logic for Repeatable Mustache

Conditional logic is also available for collections and spreadsheets, similar to the conditional logic for a standard mustache tag. However instead of having two conditional actions up our sleeve we only have one for repeatable mustache actions.

Using the .length property we can check the collection or spreadsheet to see if it contains data. If there is no data for the specified input then we can remove any contained markup. This isn’t a widely used action, however it becomes useful when we have a collection or block that is contained within container markup that has certain styles or visual embellishments that we need to hide when the collection is unused.

For example, imagine if we have a speakers collection that populates an array of repeatable speaker cards. However we also have a hard coded headline that we wouldn’t want to display if there are no speakers to begin with. We can wrap that whole section in a .length action to check if there are any speaker collection items. If not it removes the whole speakers section.

{{ #collection.speakers.length }}
  <section class="speakers">
    <h1>Meet our Speakers</h1>

    {{ #collection.speakers }}
      <div class="speaker-card" style="background-image: url('{{{ background-image }}}')">
        <h1>{{{ name }}}</h1>
        <h2>{{{ position }}}</h2>
        <p>{{{ bio }}}</p>
      </div>
    {{ /collection.speakers }}
  </section>
{{ /collection.speakers.length }}

Global v Local Inputs

A concept not really touched on while talking about inputs is the fact that inputs are globally accessible throughout the account. So that means that if a document originating from one template is merged with a document from another template, both with a {{{ headline }}} tag, that tag has the same defined input parameters, so it can also have the same value.

However if we were to put that {{{ headline }}} tag within a collection, block or spreadsheet, that input is no longer recognised in the global space, its context has been changed to be locally accessible and it is bound to that repeatable tag it resides in. So this means we can have many repeated versions of a collection, each with their own defined headline.

If an input is initially defined globally and is utilised at a later date within a collection that input’s parameters are correctly ported across. However the reverse isn’t possible because a locally defined input will only work from that particular place it was defined at and therefore needs to be defined again.

global local inputs

In saying that it is not recommended to use globally defined inputs within the local context of a collection, block or spreadsheet. While it does work normally, some issues do arise when multi-editing and merging documents. Sometimes local inputs, with the same name of global inputs try to merge, with varying results.

So if we had a headline globally and we wanted a locally accessible headline, we can prefix that headline, this would then mean we’re defining a new tag name.

<h1>{{{ headline }}}</h1>
{{ #collection.speakers }}
  <h1>{{{ speakers-headline }}}</h1>
{{ /collection.speakers }}