Liquid Tutorial 6 - Logic
Use if statements and case statements to control what happens based on context
Written By Matt Jones
Last updated About 1 month ago
Introduction
Some of the examples in this article use data from the URL of the page, which is covered in the previous tutorial Liquid Tutorial 5 - Accessing data from the page and URL however, you should be able to handle this article first if you wish to study in a different order.
So far we have focussed on how to create variables, access those variables and carry out small modifications to the data using filters.
In this article we will look at how to use logic. Think of logic as a decision-making process.
For example, returning to the calculator in the last tutorial’s activity, it would be helpful if the Liquid could decide not to carry out the calculation and show the answer until the input data is provided.
It would also be helpful in the same example to be able to carry out multiple operations instead of simply always multiplying - it would be great to let the user choose the operation +,-,*./ and have the Liquid decide the correct filter according to that input.
There are a few different choices of tags we can use to carry out logic. It’s possible for them all to achieve similar objectives, but by learning alternative ways to write the code, you can find shorter or more elegant ways to write it- this can increase readability and make the code easier to maintain.
A reference for different logical tags is available here:
https://documentation.platformos.com/api-reference/liquid/flow-control
If / endif statements
If statements are the bread and butter of logic in Liquid. The simplest form consists of two tags working together:
Example{% if context.location.search.my_favourite_number < 5 %}
You like small numbers!
{% endif %}
{% if context.location.search.keyword %}
{% comment %} Search something! {% endcomment %}
{% endif %}if is used to start the logic; endif is used to finish the logic.
Conditions
You’ll notice that the if tag has a parameter - this is called a condition. The Liquid checks to see if the condition is met; if it is, the code within the two tags is rendered; if it is not met, the code between the tags is completely skipped. Conditions can be used to make comparisons between two variables or between a variable and a given value.
Comparisons can be made using the following symbols:
==,!=,<>: equality and inequality (the latter two are synonyms)<,<=,>,>=: less/greater-thanThere are also special values
emptyandblank(unquoted) that you can compare arrays to; the comparison istrueif the array has no members. See Liquid Tutorial 10 - Truthiness in Logic: using Liquid to determine if a field is empty or blankcontains: similar to Ruby'sinclude?method, implemented on strings, arrays, and objects. For strings, it checks if the given characters on the right are included exactly at least once in the characters on the left. For arrays, it checks if any of the items in the array on the left match exactly the characters on the right. (If the left argument is a string and the right isn't, it stringifies the right.)
Always be mindful of the Liquid syntax. For example, {% if rs.enabled == 'true' %} will work while {% if rs.enabled=='true' %} (without the spaces) will not.
Simply writing the name of a single variable for a condition carries out a check to see if the variable’s value is truthy. This is covered in more detail in a later tutorial: Liquid Tutorial 10 - Truthiness in Logic: using Liquid to determine if a field is empty or blank
Example{% if context.location.search.keyword %}
{% comment %} Search something! {% endcomment %}
{% endif %}You can use as many conditions as you like in an if statement. If you do, you should join them using either an and or or keyword. Check the HTML Result tab to see the effect.
Example{% assign a = 3 %}
{% assign b = 7 %}
{% if b > a and b < 10 %}
1st example is true
{% endif %}
{% if b < a and b < 10 %}
2nd example is true
{% endif %}
{% if a > b or a == 3 %}
3rd example is true
{% endif %}
{% comment %} Probably best not to do this, but useful to understand: {% endcomment %}
{% if a > b or a == 3 and b == 8 %}
4th example is true
{% endif %}Results explained:
b is indeed greater than a AND b is indeed less than 10, so the if statement is passed.
b is NOT less than a, so the condition has already failed. There is no need to check the second condition.
a is NOT greater than b, but it doesn’t matter, because the or keyword will allow the overall statement to still pass if the second condition is passed, which it is: a does indeed equal 3.
This is the tricky one. AND statements in Liquid are always resolved first before OR statements. So first we check the conditions either side of the AND, and since the 3rd statement fails, the conditions effectively resolves to:
{% if a > b or false %}- both of which fail. We wouldn’t recommend writing code like this as it’s inevitably going to confuse either you, or someone who hasn’t studied so hard. Instead, why not write like this:
Example{% if a == 3 and b == 8 %}
{% assign first_condition_passed = true %}
{% endif %}
{% if a > b or first_condition_passed == true %}
4th example is true
{% endif %}It’s longer to write, but much quicker to read.
You may be familiar with a similar concept in JavaScript, but in JavaScript this can be made clearer with brackets- not possible in Liquid.
Else and Elsif
You can add further tags to the if statement to explain what alternative decisions should be made if the original condition is not met. An elsif statement allows you to give alternative conditions and outcomes. And else statement allows you to run code if none of the previous statements pass.
Example{% assign a = "Hello World" %}
{% assign b = 7 %}
{% if a contains "World" %}
1st example is true
{% elsif a contains "Siteglide" or b < 10 %}
2nd example is true
{% elsif a contains "Hello" and "b != 7 %}
3rd example is true
{% else %}
None of the examples are true!
{% endif %}Liquid will resolve step by step in order like this:
1st condition passes
2nd statement passes, because despite the first condition failing, the second condition passes (OR logic)
3rd statement fails because 2nd condition fails (AND logic)
The else tag is never checked because a previous statement was passed. Else code is only run if all previous statements failed.
If you’re used to JavaScript, notice in Liquid it’s written elsif rather than two separate words in JS else if.
Case / when / endcase statements
Case statements are similar to switch in JavaScript, but their syntax is a little simpler.
Sometimes elsif statements achieve the code’s objective, but start to become repetitive or hard to read. Sometimes a case statement achieves the same thing more elegantly. Case statements can only really be used for conditons where a single variable is needs to match other given variables exactly with ==. Consider the following example:
Example{% if context.location.search.filter == "a" %}
Do something 1
{% elsif context.location.search.filter == "example" or context.location.search.filter == "b" or context.location.search.filter == "c" %}
Do something 2
{% elsif context.location.search.filter == "d" %}
Do something 3
{% elsif context.location.search.filter == "e" %}
Do something 4
{% elsif context.location.search.filter == "f" %}
Do something 5
{% elsif context.location.search.filter == "g" %}
Do something 6
{% elsif context.location.search.filter == "h" %}
Do something 7
{% else %}
No conditions match. Do nothing.
{% endif %}This works, but there are two reasons which case would help simplify here:
Each time a statement is written, we need to repeat
context.location.search.filter which is tedious to write and read.
In the 2nd statement, we also need to repeat context.location.search.filter for each condition. When you’re reading the repeated words, it becomes harder to see the conditions themselves
Case is more elegant because it avoids repetition and the meaning is clearer:
Example{% case context.location.search.filter %}
{% when "a" %}
Do something 1
{% when "example" or "b" or "c" %}
Do something 2
{% when "d" %}
Do something 3
{% when "e" %}
Do something 4
{% when "f" %}
Do something 5
{% when "g" %}
Do something 6
{% when "h" %}
Do something 7
{% else %}
No conditions match. Do nothing.
{% endif %}No result is given for either example, as it would depend on the URL given.
Note no code needs to be entered between the case tag and the first when tag.
Also note you can still use else when all statements prior to it fail.
Unless/ Endunless
Unless tags can also be used, though it is rarely helpful to use them. They are the opposite of if, so whatever combination of conditions would fail an if statement would pass an unless statement.
A rare example where this is useful is if you mostly want to do one thing, but skip on a rare occasion, e.g. you are writing JSON via Liquid and want to skip a comma on the last iteration of a loop.
Example{% assign array = "apples,oranges,pears,grapes,pineapples,kiwis" | split: "," %}
[
{% for fruit in array %}
"{{fruit}}"{% unless forloop.last %},{% endunless %}
{% endfor %}
]Another situation where unless is useful, is when you need to check the opposite of contains. Since there is no “does not contains” comparison available, you can use the unless tag and contains together to check this.
Example{% assign message = "Hello World" %}
{% unless message contains "Goodbye" %}
They are not yet saying goodbye.
{% endunless %}Using the return tag to end Liquid execution
The return tag can be used to end the current Liquid execution. It can therefore be quite useful for simplifying authentication logic.
Let’s consider a situation where several checks must be done on a user’s identify before they are allowed to access the page. The whole page’s Liquid would need to be wrapped inside the if statement, meaning the if and the endif would be far apart, which could become tricky to read later, especially if indentation was missed:
Example{% if context.location.search.key == "1234455" and context.exports.current_user.secure_zones contains "55665" %}
Do lots of secure things - load the whole rest of the page.
{% endif %}To simplify this, we can get the page to stop rendering if certain conditions are met, meaning we can separate the authentication logic from the rest of the page:
Example{% unless context.location.search.key == "1234455" and context.exports.current_user.secure_zones contains "55665" %}
{% return false %}
{% endunless %}
Do lots of secure things - load the whole rest of the page.Once a return tag has been run, the server will stop processing the Liquid in the current file. It can be used to cancel loading the rest of the page if you are working in a page, or cancel the current partial if you’re working in a partial Liquid file e.g. a Studio Section or dynamic module layout.
Activity - Finish the Calculator
The answer to the last tutorial’s activity builds a calculator which allows the user to multiply two numbers given by the user.
Starting with last time’s code:
Example<h1>{{context.page.meta_title}}</h1>
<form action="{{context.location.pathname}}" method="GET">
<input type="number" name="number_1" placeholder="Enter a number">
<input type="number" name="number_2" placeholder="Enter a number you wish to multiply by">
<input type="submit" value="submit">
</form>
{% assign search = context.location.search %}
{% assign number_1 = search.number_1 %}
{% assign number_2 = search.number_2 %}
Your answer is: {{number_1 | times: number_2 }}.This time, can you modify the calculator to:
Include a <select> HTML element in the form for choosing an operation +,-,* or /
Use an appropriate logical statement to run a different filter on the numbers depending on the operation passed to the search parameters.
Use an appropriate logical statement to avoid running the calculation until all the required inputs have been passed to the URL.
For a bonus point (but no-one is counting!), can you consider a calculation edge case which will throw an error or unexpected result and add an extra logical statement to handle it?