Introduction
Liquid is the new layout engine for sites on the platform. It allows for great flexibility in choosing the appearance of your site by leveraging clean CSS friendly HTML code with simple to use templating features. By using Liquid markup, all aspects of the pages presented to your users are under your control.
What is the Liquid markup?
Liquid is a very simple templating engine designed so that you have simple markup and beautiful results. Here is a brief sample of a liquid template:
<div>
<a href="{{product.url}}">
<img alt="{{product.name}}" src="{{product.smallImage}}"/>
</a>
<div>
Price:<strong>{{product.price}}</strong>
{%if product.stock >0%}
{{product.stock}}
{%else%}
Out of Stock
{%endif%}
</div>
As you can see, the templating engine can output variables inside the template using the {{variable}} statement, can have conditionals (using the {% if … %} statement )and more.
You can read more about Liquid markup here.
Modules with Liquid Markup
The first thing introduced with Liquid markup in the system is a new module definition.
Previously the module definitions looked like:
{module_product,catalogID,ProductID,targetFrame}
Now the module syntax is much cleaner and simpler:
{module_product catalogId="102" productId="530" template="/_System/ModuleTemplates/Shop/productSmall.html"}
As you can notice, now the parameters are named, meaning that you no longer need to remember the order of the parameters and it increases readability. Also the new parameter separator is now space and the value of a parameter must be between quotes.
Also, there is a new parameter called "template". This parameter specifies what page to use as rendering template for the module. You can specify any number of templates for the module, eliminating thus the number of templates that you can use on a module. You can have as many as you want.
The only restriction remains the fact that the e-commerce templates must be placed in the "/_System/ModuleTemplates/Shop/" folder.
Now, let's analyze the if the content of template file for this module. Let's assume that the "/_System/ModuleTemplates/Shop/productSmall.html" contains the following:
<div>
<a href="{{product.url}}">
<img alt="{{product.name}}" src="{{product.smallImage}}"/>
</a>
<div>
Price:<strong>{{product.price}}</strong>
{%if product.stock >0%}
{{product.stock}}
{%else%}
Out of Stock
{%endif%}
</div>
As you guessed, the {{product.name}} will render the name of the product, {{product.url}} will render its URL, etc. But how does that work?
Every module will generate a specific entity that can be used in the module. In this case the will generate the product object which is populated automatically with all of its corespondent properties: name, URL, price, etc.
In the template's body you can use the module's generated entity and all of its properties as shown above.
Please see attached (LiquidDocumentation.zip) for a complete list of modules, their syntax and definition. Note that some module names have changed so that it's easier to understand their role and function.
Let's take a look at another module: {module_productList} (used to be called ). Here's how you could insert it in a page:
{module_productList catalogId="102" template="/_System/ModuleTemplates/Shop/productList.html"}
Let's imagine that its template in the productList.html file looks something like:
{%for prd in products %}
<li>
<a href="{{prd.url}}">{{prd.name}}</a>
</li>
{%endfor%}
</ul>
As you can see, here things are a bit different. Since the module will generate a list of products, the entity generated by the module is called {{products}}. And since it's a collection, you can loop through it using the {% for … in … %} … {% endfor %} statement.
The products contains a collection of products that you can use. The type of the product entity is the same one as the one generated by , so you can use here all of its properties (just like you would use them in the productSmall.html template.
Collections and Navigation
Every time a module creates a collection of objects (like in the case of {module_productList}, there are additional things that you can do with that module.
For instance, a {{nav}} entity is generated with every collection. This will allow you to do very flexible and creatin navigations when listing your collections.
For example, the {module_productList} will generate a {{products}} entity, but on that collection you will have a {{products.nav}} object generated with it. That object contains information like:
- How many products are inside that collection: {{products.nav.total}}
- Links to the next and previous pages: {{products.nav.nextUrl}},{{products.nav.previousUrl}}
- etc.
So you could do very interesting things in your layout like creating a paged navigation.
Here's how you do it. In the page you insert the {module_productList} with an additional paremeter called itemsPerPage. This tells any module with a collections how many products to retrieve at a time. It also prepares the {{nav}} object withe the links required to create pagination.
{module_productList catalogId="102" itemsPerPage="4" template="/_System/ModuleTemplates/Shop/productList.html"}
Now the {{products}} entity will collect only 4 products at a time and you can add in you own navigation. By default the value of the itemsPerPage is its maximum possible value (200). Here's an example with a very basic navigation that we'll be adding in the productList.html template:
{%for prd in products %}
<li>
<a href="{{prd.url}}">{{prd.name}}</a>
</li>
{%endfor%}
</ul>
<div>
<a href="{{products.nav.previousUrl}}">Previous</a>
<a href="{{products.nav.nextUrl}}">Next</a>
</div>
So when you will test this page, you will be able to navigate through the products 4 at a time.
There is a little trick that you should know. The {{nav}} entity is smart enough to understand if you are at the first or last page of the product collection and in that case it will generate empty URLs so that you can hide them (you would not want a Previous link if you're already on the first page. So you can update the code in the "productLIst.html" so that it looks like this:
{%for prd in products %}
<li>
<a href="{{prd.url}}">{{prd.name}}</a>
</li>
{%endfor%}
</ul>
<div>
{%if products.nav.previousUrl != "" %}
<a href="{{products.nav.previousUrl}}">Previous</a>
{%endif%}
{%if products.nav.previousUrl != "" %}
<a href="{{products.nav.nextUrl}}">Next</a>
{%endif%}
</div>
Modules in modules
But what if I want to re-use the template I created for the in the first section of this article? You can use modules inside other modules. Here's how its done. In the "productList.html" template we add and pass some parameters to it.
{%for prd in products %}
<li>
{module_product productId="{{prd.id}}"
catalogId="{{prd.catalogId}}"
template="/_System/ModuleTemplates/Shop/productSmall.html"}
</li>
{%endfor%}
</ul>
<div>
{%if products.nav.previousUrl != "" %}
<a href="{{products.nav.previousUrl}}">Previous</a>
{%endif%}
{%if products.nav.previousUrl != "" %}
<a href="{{products.nav.nextUrl}}">Next</a>
{%endif%}
</div>
As you can see, we're passing the product ID and the catalog ID to and now that module is going to render each product in the collection using the productSmall.html template.
Re-using templates
If you have repeating sections of code (HTML or Liquid markup) that you use in several locations, then you might want to consider includes.
For instance if you have a section of HTML code that you need to re-use, put it in a file like "/_System/ModuleTemplates/Shop/myCustomCode.html":
Here is a custom code that I want to put throughout my pages.
</div>
You will be able to include it in all of your pages, like this (we'll include it in the productList.html template):
<ul>
{%for prd in products %}
<li>
{module_product productId="{{prd.id}}"
catalogId="{{prd.catalogId}}"
template="/_System/ModuleTemplates/Shop/productSmall.html"}
</li>
{%endfor%}
</ul>
<div>
{%if products.nav.previousUrl != "" %}
<a href="{{products.nav.previousUrl}}">Previous</a>
{%endif%}
{%if products.nav.previousUrl != "" %}
<a href="{{products.nav.nextUrl}}">Next</a>
{%endif%}
</div>
The {% include ... %} statement allows you to include code in any template just as if you would write it in the template itself. It will give you a very flexible tool for re-using code.
Customizing the overall shop layout using Liquid markup
Once you turn Liquid markup on, there is one page the customizes your overall shop look & feel.
That page is "/_System/ModuleTemplates/Shop/shopLayout.html". This page is a Liquid template that will control how your e-commerce pages will render. This means controling the pages that will render an individual catalog rendering or an indvidual product.
Each time a catalog or a product URL is requested, this page is used to render it. So if you have a catalog called "catalog1" and a product inside that catalog called "product1" when accessing wither the http://mysite.com/catalog1 or http://mysite.com/catalog1/product1/ the page being loaded is shopLayout.html.
This page behaves like a module who receives either a catalog or a product as a parameter (depending if you're accessing a catalog or a product).
Let's take a look at a very simple possible content for the shopLayout.html template
{module_catalogList template="/_System/ModuleTemplates/Shop/catalogList.html" parentCatalogId="{{catalog.id}}" sortBy="weighting desc"}
{%if product %}
{module_product productId="{{product.id}}"
catalogId="{{catalog.id}}"
template="/_System/ModuleTemplates/Shop/productLarge.html"}
{%else%}
{module_productList
template="/_System/ModuleTemplates/Shop/productList.html"
catalogId="{{catalog.id}}"
sortBy="weighting desc"
itemsPerPage="4"}
{%endif%}
</div>
Let's try to understand how this works.
Every time a catalog is requested (ex: http://mysite.com/catalog1) then a {catalog} entity is created, so that you can use in this template all of its properties or pass paramaters to other modules that you might include.
In the same time when (and only when) you're also going directly to a product (ex: http://mysite.com/catalog1/product1) then a {product} entity is also created. If not, that entity is not created. This way you can decide what you want to render on this page. In the above example, the {module_catalogList} will be render no matter if we are viewing just a catalog or a product.
But if a specific product is requested (ex: http://mysite.com/catalog1/product1), then the {product} entity will be generated and the will render that product. if on the other hand the user accesses just the catalog, (ex: http://mysite.com/catalog1), then what we want to render is the entire list of the products inside that catalog, listing 4 products at a time sorted descendingly by weighting in that catalog.
As you can see this is a very flexible mechanism for you to use in many flexible and diverse ways so that you can have full control over the overall shop experience (you can add in breadcrumbs, other catalog or product views as you need them).
Also, don't forget that the template that will contain the shopLayout.html is the one corresponding to the catalog you're viewing (you can specify what template you want for each catalog), just like when you were using the Overall Layout from admin. Actually, the shopLayout.html is the equivalent of the Overall Shop layout in the old rendering engine.
The shopLayout.html is currently the only template who's name and path you cannot change. You can only have a single shopLayout.html file in your site, but don't forget that using the {% include ... %} statement and conditionals you could emulate as many as you can.
Advanced scenarios
Here are some situations where the Liquid markup can be extremely helpful:
Using a complex module or template multiple times on the same page
In order to accommodate the functional and visual requirements of your clients, the layouts of even the simplest of modules can get very complex, containing complicated markup, linking external scripts and stylesheets. One can place in the template link and script html elements, even if not strictly correct from a web development standpoint. However, in the case of multiple templates being displayed at the same time (as is the case of a list of products, for example) the resulting page output would be littered with repeated content or references to external files.
The solution for this problem is the section tag. Encapsulate your script and stylesheet declaration within a section and they will be rendered in the head element of the page, once each distinct resource.
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.4/jquery.min.js">
</script>
{% endsection %}
The code above will use the popular jQuery library, by loading it only once in the head of the page, no matter how many times the code itself will appear on the page.
Strictly controlling the output of the Liquid markup
When writing Liquid markup, we tend to use tags on their own lines so that the templates remain readable and easy to maintain. However this leads to empty lines being rendered on the output page.
{%if get.extraInfo %}
<span>Extra information is available</span>
{%endif%}
</div>
The code above will appear on a client page as this markup (assuming the page has a parameter called extraInfo set):
<span>Extra information is available</span>
</div>
The empty lines between the div and the span are not displayed differently on the client browser, but some designers want to strictly control the markup, for reasons of validation, security or simply for esthetic reasons.
The way one does this using Liquid markup is by adding a minus (-) sign before ending the tag. Here is the Liquid markup that would render without the empty lines:
{%if get.extraInfo -%}
<span>Extra information is available</span>
{%endif-%}
</div>
Comments