_images/airmad.svg

Airmad

Airtable in Automad — a fexible yet simple to use Automad extension that let’s you easily integrate Airtable bases into your site by using Handlebars templates.

Get Started

Airtable is a great tool to quickly create your own database using a intuitive UI. While the possibilities of structuring data go far beyond the capabilities of Automad as a blogging platform, you might find out that Airtable lacks of flexibility and design options when it comes to sharing tables publicly. This is where Airmad comes in. The concept is rather simple. Airmad pulls a table — and optionally also its linked tables — using Airtable’s REST API. To speed things up and align them with the user experience of a small and lightweight Automad site, all retrieved recordes are cached on your server. Updated data is pulled from time to time.

Attention

Airmad requires your webserver to run PHP 7+ in order to work properly!

Installation

Airmad can be installed by using the Automad dashboard. However in case you would like to install the package by using Composer, just run the following command on your command line:

$ composer require airmad/airmad

Configuration

Airtable requires an API token to authenticate when accessing bases using their REST API. In case you don’t have one, you can easily create one on your Airtable profile page. After successfully creating such token, it has to be added to Automad’s config/config.php file. That can be done by navigating to System Settings > More > Edit Configuration File in the Automad dashboard as demonstrated below. Aside from the authentictaion, there you can also configure the Airtable cache lifetime in seconds.

{
  "AIRMAD_TOKEN": "keyXXXXXXXXXXXXXX",
  "AIRMAD_CACHE_LIFETIME": 43200,
  ...
}

Usage

Airmad can either be used in template files as part of a theme or, as recommended, in a snippet block. The latter one allows for integrating Airmad into any existing theme that supports Automad’s block editor. The markup looks as follows:

Attention

You can simply paste an Airmad snippet directly into a code field of the new Template Snippet block on any page in the Automad dashboard.

<@ Airmad/Airmad {
    base: 'appXXXXXXXXXXXXXX',
    table: 'Products',
    view: 'Grid view',
    linked: 'Type',
    template: '
        <div class="card">
            <div class="card-content uk-panel uk-panel-box">
                <div class="uk-panel-title">
                    {{ fields.Name }}
                </div>
                <p>
                    {{# fields.@.Type }}
                        <i>{{ Name }}</i>
                    {{/ fields.@.Type }}
                </p>
            </div>
        </div>
    ',
    filters: 'Name, Type',
    limit: 20,
    page: @{ ?page | 1 }
} @>

The code above doesn’t produce any output. Instead it populates some Runtime variables that can be used in the Automad template to at any point after the Airmad instance above. To display the generated output, the :airmadOutput variable can be used in a template for example as follows.

<div class="cards grid am-stretched">
    @{ :airmadOutput }
</div>

Attention

In case you want to use multiple Airmad instances on one page, you will have to define unique prefixes for each one in order to avoid conflicts between them. Read more about using multiple instances below.

Options

The example above shows a typical use case of an Airtable integration. Find below a list of all availabe options.

Name

Description

base

The Airtable base ID

table

The main table to be used to pull records from

view

The view of the main table to be used

linked

A comma separated list of tables that are linked to a field of the main table records — note that is only required to list linked tables here that include information that you want to display. In case the field name differs from the actual table name to be linked, it is also possible to pass a list of strings like fieldName1 => tableName1, fieldName2 => tableName2 to the parameter to link such fields to any table.

template

The Handlebar template to be used to render a record — can be either a string, a variable containing a string or a file path

filters

A comma separated list of fields that can be used to filter the records by — check out the examples below for more information about filtering

limit

The maximum number of records to be displayed on a page

page

The current page of records (pagination)

prefix

An optional prefix for the generated runtime variables instead of the default :airmad — it is required to define unique prefixes, in case more than one Airmad instance is used on a page

Runtime Variables

Aside from the output, Airmad provides more variables as shown in the table below.

Name

Description

:airmadOutput

The rendered output of the table records

:airmadCount

The number of found records

:airmadPage

The current page number — this has to be seen in context to the limit of items displayed on a page

:airmadPages

The amount of pages the records are spread over, also related to the limit option

:airmadMemory

The max memory used by Automad in bytes

Attention

Note that you can define an unique prefix to be used instead of :airmad* in the Airmad options when creating a new instance.

Multiple Instances

As soon as you want to use filters and select dropdowns to let a user control the displayed set of records on a page, you will have to use multiple instances of Airmad on one page. For example one instance request all records of a fictional table called Type to generate a list of all existing product types in your database, while another one gets the actual products for example from a table called Products. To avoid overwriting the output the first table with the output of the second one, the generated runtime variables need to have a unique prefix that can be defined in the options by using the prefix parameter.

 <@ Airmad/Airmad {
     base: 'appXXXXXXXXXXXXXX',
     table: 'Type',
     view: 'Grid view',
     template: '<option value="{{ fields.Name }}">',
     prefix: ':type'
 } @>
 @{ :typeOutput }

 <@ Airmad/Airmad {
     base: 'appXXXXXXXXXXXXXX',
     table: 'Products',
     view: 'Grid view',
     template: '<option value="{{ fields.Name }}">',
     prefix: ':products'
 } @>
 @{ :productsOutput }

Templates

As mentioned earlier, Airmad uses Handlebars templates to render record data. While iterating table records, all record data is exposed to the engine and can be accessed by using the normal variable tags. The main items here are the id, the fields and the createdTime. The fields item actually contains all table fields entered by you. For example to get the Name of a record, you can simply use {{ fields.Name }} in a template. Aside from the default tags, Airmad provides some other useful helpers to let you easily use fields in linked tables or build slideshow.

Image Sliders

In case your table has an attachement field, you can use the {{#slider fields.images}} helper function to create an image slider containing all provided images as that are listed in a field called fields.images. By default the slide will have an aspect ratio of 1:1 — in other words a height of 100% relative to the width. You can pass an optional second argument to the helper to define a custom height as follows:

{{#slider fields.images 75%}}

Linked Tables

In case you have fields that actually link to other tables in your base, the content of such a field is just a bunch of record IDs. In most cases you would want to be able to actually get the values of the one or more fields of that record. Therefore Airmad adds a dedicated fields to your data model at runtime called fields.@. The @ field contains all referenced records in linked tables. The example below demonstrates the usage of such fields.

To simply get the IDs of the records in a linked table, you can just loop over the list of IDs as usual.

{{# fields.Type }}
    {{ . }}
{{/ fields.Type }}

Instead of just getting the ID, you can directly loop over a list of the linked records by inserting a @ into {{# fields.Type }} like {{# fields.@.Type }}. It is important to understand that the name after the @ is here again the name of the field and might differ from the actual table name.

 {{# fields.@.Type }}
     <i>{{ Name }}</i>
 {{/ fields.@.Type }}

Active Filters

When building dropdown menus or similar to filter the set of elements, it is imortant to know what filter is currently active. Therefore Airmad the active field to any record that appears as value for a table filter in the query string. The field can be used as follows:

<option value="{{ id }}" {{#if active}}selected{{/if}}>
    {{ fields.Name }}
</option>

Filters

Searching and filtering are essential functions for displaying database content. In Airmad filtering records is pretty straight forward. The following example demonstrated the basic idea:

 <form action="">
     <input type="text" name="Name" value="@{ ?Name }">
     <input type="text" name="Type" value="@{ ?Type }">
 </form>
 <ul>
 <@ Airmad/Airmad {
     base: 'appXXXXXXXXXXXXXX',
     table: 'Products',
     view: 'Grid view',
     linked: 'Type',
     template: '<li>{{ fields.Name }}</li>',
     filters: 'Name, Type',
     limit: 20,
     page: @{ ?Page | def(1) }
 } @>
 </ul>

In the snippet above, we have a simple form at the top including two input fields with the names Name and Type. The Airmad instance below that form has those names defined as filters as you can see in the highlighted line. Note that since in this example Type is a linked table, defining the linked parameter allows for searching in linked records as well.

Autocompletion

To enhance the user experience for your visitors, you might want to provide an autocompletion list of Type names for the second input field. You can simply use a second Airmad instance to pull all type names from the Type table and populate such a list with the Name field of each record. In the following example we use a datalist for such purpose.

Warning

Note in the snippet below that this time the prefix parameter must be set to a unique value to avoid conflict between both Airmad instances.

 <form action="">
     <input type="text" name="Name" value="@{ ?Name }">
     <input type="text" list="types" name="Type" value="@{ ?Type }">
     <@ Airmad/Airmad {
         base: 'appXXXXXXXXXXXXXX',
         table: 'Types',
         view: 'Grid view',
         template: '<option value="{{ fields.Name }}">',
         prefix: ':type'
     } @>
     <datalist id="types">
         @{ :typeOutput }
     </datalist>
 </form>
 <ul>
 <@ Airmad/Airmad {
     base: 'appXXXXXXXXXXXXXX',
     table: 'Products',
     view: 'Grid view',
     linked: 'Type => Types',
     template: '<li>{{ fields.Name }}</li>',
     filters: 'Name, Type',
     limit: 20,
     page: @{ ?Page | def(1) }
 } @>
 </ul>

Example

The following example is supposed to wrap all features of Airmad like getting records, filtering and building a pagination. To allow for quick testing, the base for this example is the Project tracker database that serves as sample content when creating a new account on Airtable. Therefore it should be easy to just copy and paste the code — by replacing the app ID of course — after setting up authentication.

Attention

Make sure that you already have added the AIRMAD_TOKEN to your configuration as described in the Get Stared guide. And don’t forget to replace the base ID in the snippet below with the one in your API documentation!

To be easily understandable, this example code is boken down into three section. You can simply paste all sections together into a Template Snippet block.

Filters

The first part is creating the filter menu. Note that the naming of the input fields is essential here! In our example we want to filter the records by the Client field that is linked to the Clients table (note the “s” in the table name).

<form action="">
    <input type="text" name="Name" value="@{ ?Name }">
    <input type="text" list="clients" name="Client" value="@{ ?Client }">
    <@ Airmad/Airmad {
        base: 'appXXXXXXXXXXXXXX',
        table: 'Clients',
        view: 'All clients',
        template: '<option value="{{ fields.Name }}">',
        prefix: ':clients'
    } @>
    <datalist id="clients">
        @{ :clientsOutput }
    </datalist>
    <button type="submit">Filter</button>
</form>

Listing Records

The second part is building the actual list of record. Again, the mapping of linked tables is important here! The code below will generate a grid of record including a little slider to be used with with the Standard theme.

<@ Airmad/Airmad {
    base: 'appXXXXXXXXXXXXXX',
    table: 'Design projects',
    view: 'All projects',
    linked: 'Client => Clients',
    template: '
        <div class="card">
            <div class="card-content uk-panel uk-panel-box">
                <div class="uk-panel-teaser">
                    {{#slider fields.Project images 75%}}
                </div>
                <div class="uk-panel-title">
                    {{ fields.Name }}
                </div>
                <p>
                    {{# fields.@.Client }}
                        <b>{{ Name }}</b>
                    {{/ fields.@.Client }}
                </p>
            </div>
        </div>
    ',
    filters: 'Name, Client',
    limit: 8,
    page: @{ ?Page | def(1) }
} @>

<div class="cards grid am-stretched">
    @{ :airmadOutput }
</div>