Looping through objects and arrays

Loop through and repeat content from an array or object in your personalized message content.

You can use the #each operator to loop through and repeat content from an array or object. For example, you can show a user all the items in their shopping cart, including quantity, description, and price.

Specify the key of the array or object that you want to start your loop from using {{#each <property>}}. You can access properties within the loop with the HandlebarsHandlebars is Airship’s templating language for personalization. Handlebars expressions use double curly braces wrapped around a content template, ranging from a simple variable, e.g., {{first_name}}, to complex evaluations of personalization data. expressions on this page.

 Note

You cannot limit loops using Handlebars. If you want to limit iterations in a loop, you must limit the array or object data that you pass to Airship.

Setting a loop limit

When you use #each, you can limit the number of loops to perform, making sure that your message is a reasonable length.

Set a limit using #each (limit <property> <integer>).

We think you might like:
{{#each (limit array_of_objects 2) as |obj|}}
{{obj.name}}: {{obj.desc}}
{{/each}}

Setting context using #with

Use {{#with}} to set the context within your template. This can help you access nested keys without having to repeat the parent object’s path.

For example, if your audience’s shipping information is nested in an object called shippingAddress, your code might look like this:

{{shippingAddress.street}}
{{shippingAddress.state}}, {{shippingAddress.zipCode}}

You then might use the with helper to limit context and simplify your template like this

We'll ship your package to:
{{#with shippingAddress}}
   {{street}}
   {{state}}, {{zipCode}}
{{/with}}

Creating and concatenating arrays

When constructing new arrays or concatenating existing arrays, the following operators are available:

  • {{$array <object...>}} — Creates a new array from the given items. Any number of items may be passed as parameters, e.g., {{$array "a" "b" "c"}} creates a new array containing ["a", "b", "c"].
  • {{$concat <arrays...>}} — Creates a new array from a given list of arrays.

The with helper can be useful here to assign these created objects to new variables:

{{#with ($array "a" "b" "c") as |arr|}}
{{#each arr}}{{.}}{{#unless @last}}, {{/unless}}{{/each}}
{{/with}}

…which will yield a, b, c.

Checking for presence

The $contains operator can be used to check if a value is present in an array.

  • {{$contains <array> <search_item>}} — Returns a boolean value representing if search_item exists in array.
{{#if ($contains favorite_movies "Brazil")}}
You must be a Terry Gilliam Fan.
{{/if}}

Working with key/value objects

The $merge and $pick operators can help you construct new key/value objects from existing ones.

  • {{$pick <object> <keys...>}} — Constructs a new object from keys picked from object. If a key is not found in the object, it will be omitted. You may specify as many keys as you like, e.g., {{$pick obj "key1" "key2"}}.
  • {{$merge <objects...>}} — Constructs a new object by merging the given objects. They are merged only on their top-level keys, and the right-most object wins if the same key is present in multiple objects.
{{#with ($pick ($merge obj1 obj2) "key1" "key2") as |newObj|}}
{{#each newObj}}
* {{@key}}: {{.}}
{{/each}}
{{/with}}

Filtering empty values

You can use the $filterEmpty operator to filter any empty values from an array or key/value object. When filtering a key/value object, values are evaluated for emptiness:

{{#with ($filterEmpty ($array "a" "b" "" "c")) as |arr|}}
{{#each arr}}{{.}}{{#unless @last}}, {{/unless}}{{/each}}
{{/with}}

…which will yield a, b, c.

Getting the number of items in objects and arrays

Using the length property on an array, or the size property on a map or object, you can get the number of items or number of keys, respectively.

{{#gt array.length 3}}
Array length was greater than 3!
{{/gt}}

{{#gt object.size 3}}
Object had more than 3 keys!
{{/gt}}

Referencing properties in objects and arrays

Because your loop reference begins from the object or array specified by #each, you don’t need to provide the full path of the property that you want to reference, just the path to the key within the current iteration of the loop.

For example, if looping through an array of objects called suggestedProducts, you could specify the keys within each object — qty and productName.

{{#each suggestedProducts}}
{{qty}}x {{productName}}
{{/each}}

You can also reference properties of the array itself within the context of the loop.

  • {{.}} — Accesses the current item in the array, generally for simple arrays of strings or integers.

  • {{@index}} — References the current array index in the loop.

  • {{@key}} — Provides the key name or index location of the current loop iteration.

  • {{@root}} — Accesses the root element properties while you iterate in the context of any nested or child elements.

  • {{@first}} — Returns true for the first element in the loop and can be used with If/Else statements. For example, {{#each array}} {{#if @first}} Hello! {{/if}} {{/each}} will include “Hello!” in front of the first object in your array.

  • {{@last}} — Returns true for the last element in the loop and can be used with If/Else statements. For example, {{#each array}} {{#if @last}} Goodbye! {{/if}} {{/each}} will include “Goodbye!” in front of the last object in your array.

  • {{this}} — Limits the context to the current object iteration of the loop. Use dot notation to reference a key in the object, i.e., {{this.key}}.

    In general, you don’t need to use this unless you have a duplicate key outside the loop and the key in your loop is optional. In such a case, this prevents the template from finding and using a duplicate key outside the current iteration of the loop if the key in the loop is missing.

Reference names in a simple array:
Everybody coming to the pizza party:
{{#each pizza_partiers}}
{{@index}}: {{.}}
{{/each}}

Reference items in a user’s shopping cart as an array of objects:
Are you still interested in the following items?
{{#each cart}}
* {{qty}}x {{product}} for {{price}}
{{/each}}

Using a feed

An External Data Feed is a connection to an external API. When you send a message, Airship uses a response from that API to personalize messages. See: External data feeds.

You reference an external feed as a block, and you can use properties from your feed within the block — {{#feed "feed_name" var_in_url="value" as |alias|}}.

For example, imagine you have a feed at https://www.example.com/[[region]]/featured-products that returns an array of products that you want to display to your audience. You may want to set the region at send time (rather than using the default value from your feed), and you may want to provide an alias so that your feed properties don’t collide with attributes or custom event properties.

Your message text and Handlebars might look something like this:

{{#feed "featured_products" region="us" as |result|}}
  {{#each (limit result.products 2) as |product|}}
    {{product.name}}: {{product.price}}
    https://cool-store.tld/us/products/{{urlEncode product.slug}}/
  {{else}}
    Check back later for deals!
  {{/each}}
{{else}}
Couldn't fetch featured products!
{{/feed}}
 Note

While the feed block grants access to variables from the feed response, you can still reference variables (or merge fieldsA variable in your message or template that you want to populate with a personalized value for each member of the audience. Merge fields use Handlebars syntax — {{merge_field}}. ) that aren’t a part of the feed — attributes, custom event properties, etc. If this is something you may do, set the feed namespace using as |alias| to differentiate between properties from the feed and other personalization variables.