Looping Through Custom Attributes

Note: Some of the concepts in this article require an understanding of liquid beyond the basics. If you feel lost at any point, please take a look at the  liquid guide for designers

If you have a long list of custom attributes that you need to surface on the listing page, it can be cumbersome to manually insert each attribute in the view - especially if you are using additional if-else statements. For instance, let's say you have 10 custom attributes, and after having read our guide on  surfacing custom attributes in the booking module, you begin adding each one into the liquid view like so:
{% unless listing.properties.attribute_1 == blank %}   <p> {{ listing.properties.attribute_1 }} </p> {% endunless %} {% unless listing.properties.attribute_2 == blank %}   <p> {{ listing.properties.attribute_2 }} </p> {% endunless %} {% unless listing.properties.attribute_3 == blank %}   <p> {{ listing.properties.attribute_3 }} </p> {% endunless %}
As you can see, this approach can be messy, and very difficult to maintain. What happens if you need to update the names of these attributes, or if you discover some minor mistake in your syntax? You then need to apply a fix/update to every single block of code which is not fun. You need a more  DRY approach to surface all of your custom attributes at once.

This is where loops come in handy. Liquid, not unlike other languages, allows you to iterate over any number of items in a list, which reduces the amount of code that you have to write. For instance, let's compare this block of code to what we have above:

  {% assign displayable_properties = "attribute_1 | attribute_2 | attribute_3" | split: "|" %}<br>  {% for property in displayable_properties %}<br>    {% unless listing.properties[property] == blank %}<br>      <span>{{ listing.properties[property] }}</span><br>    {% endunless %}<br>  {% endfor %}
Our first approach has 9 lines of code, whereas our loop has only 7. And assuming you have more than 3 custom attributes, the first approach will only become longer and less maintainable. Clearly the loop is a more effective strategy.

Let's break down the loop into more detail.

The first line reads:
  {% assign displayable_properties = "attribute_1 | attribute_2 | attribute_3" | split: "|" %}
This creates a variable (displayable_properties) and assigns each of the attributes you have listed between each dividing line. If you had more attributes, you would need to add each one to this line - but note that this is the  only place where you need to list each attribute, so this approach is still far more DRY than the first. Now, in our loop, we can simply reference the variable "displayable_properties", rather than each attribute individually.

Then we have:
{% for property in displayable_properties %}<br>  {% unless listing.properties[property] == blank %}<br><br>  {% endunless %}<br>{% endfor %}
The first line (i.e. "for property in displayable properties") simply begins the loop - where you specify that for each property in your "displayable_properties" variable, we want to do something.

The second line looks a lot like the repeated blocks in our first example, where we say "Unless listing.properties.property is blank, do something".

And finally, we surface each attribute with the following:
<span>{{ listing.properties[property] }}</span>

Now, if you use the entire block highlighted above, you should see your attributes surfaced in exactly the same way as if you had included them each individually.