DotLiquid in ASP.NET Core e-commerce platform — how to handle message templates

How to use Liquid in e-commerce platform

DotLiquid is a templating system ported to the .NET from Ruby Liquid programming language. In this article, I will describe DotLiquid and show you how we used it in GrandNode — .NET Core e-commerce platform and how you can extend its functionality.

Requirements

Differences between DotLiquid and Liquid

As author says, DotLiquid was created to be as much similar as possible to core Liquid syntax. Mostly it’s true, but as you can find on the DotLiquid repository, there also exists differences.

For example if you want to force the line break that would normally follow the end of a tag, you can use a hyphen (“-”) just before closing the tag. Just like in the following example:

{% for item in Order.OrderItems -%}
<tr>
<td>{{item.ProductName}</td>
<td>{{item.UnitPrice}}</td>
<td>{{item.Quantity}}</td>
<td>{{item.TotalPrice}}</td>
</tr>
{% endfor -%}

will result in the following output, without extra line breaks and will create a two rows with products on order confirmation:

<tr>
<td>First product name</td>
<td>First product price</td>
<td>First product quantity</td>
<td>First product total price</td>
</tr>
<tr>
<td>Second product name</td>
<td>Second product price</td>
<td>Second product quantity</td>
<td>Second product total price</td>
</tr>

Real example from GrandNode

In GrandNode, DotLiquid is used to handle message templates. You can create loops and if statements. The best example to show you the possibilities of DotLiquid, is to show the Shipment delivered notification, because it contains all of that features and in the same time is much simpler than order notification.

I will remove styling, because here it doesn’t make any sense to include them.

<a href="{{Store.URL}}"> {{Store.Name}}</a> <br />
<br />
Hello {{Order.CustomerFullName}}, <br />
Good news! You order has been delivered. <br />
Order Number: {{Order.OrderNumber}}<br />
Order Details: <a href="{{Order.OrderURLForCustomer}}" target="_blank">{{Order.OrderURLForCustomer}}</a><br />
Date Ordered: {{Order.CreatedOn}}<br />
<br />
<br />
<br />
Billing Address<br />
{{Order.BillingFirstName}} {{Order.BillingLastName}}<br />
{{Order.BillingAddress1}}<br />
{{Order.BillingCity}} {{Order.BillingZipPostalCode}}<br />
{{Order.BillingStateProvince}} {{Order.BillingCountry}}<br />
<br />
<br />
<br />
Shipping Address<br />
{{Order.ShippingFirstName}} {{Order.ShippingLastName}}<br />
{{Order.ShippingAddress1}}<br />
{{Order.ShippingCity}} {{Order.ShippingZipPostalCode}}<br />
{{Order.ShippingStateProvince}} {{Order.ShippingCountry}}<br />
<br />
Shipping Method: {{Order.ShippingMethod}} <br />
<br />
Delivered Products: <br />
<br />
<table>
<tr>
<th>Name</th>
<th>Quantity</th>
</tr>
{% for item in Shipment.ShipmentItems -%}
<tr>
<td>{{item.ProductName}}
{% if item.AttributeDescription != null and item.AttributeDescription != "" %}
<br />
{{item.AttributeDescription}}
{% endif %}
{% if item.ShowSkuOnProductDetailsPage and item.ProductSku != null and item.ProductSku != "" %}
<br />
Sku: {{item.ProductSku}}
{% endif %}
</td>
<td>{{item.Quantity}}</td>
</tr>
{% endfor -%}
</table>

We love DotLiquid for its simplicity. Let’s look at the example above. Starting from the first line, we have simple fields {{Store.URL}}, {{Store.Name}}. This tokens will dynamically insert the URL and Name of the store coming from configuration. If you want to see how it’s managed from backend, jump into GrandNode source code and check the Grand.Services project -> Messages -> DotLiquidDrops -> LiquidStores.cs. Basically every token that can be used in message template, comes from this part of the solution.

Ok, we’ve started from the tokens, now we can focus on for loops. As you can see in the example, we have one loop:

{% for item in Shipment.ShipmentItems -%}
...
{% endfor -%}

This line will create single row for every shipped item. If we have one item, it will create 1 row. Simple.

Last thing that I wanted to cover is if statement.

{% if item.AttributeDescription != null and item.AttributeDescription != "" %}
<br />
{{item.AttributeDescription}}
{% endif %}

If Attribute description exists, it will be inserted in to provided token. Otherwise, this field will be empty. You can do it almost with every part of store.

There is possibility to create your own tokens. To create them, you can use the Roslyn scripts. In my example, we will create new token for order notifications. Token will insert the new type of order number. The whole example can be found below.

This script is also a perfect example of MediatR library usage in GrandNode. We’ve published an article about it, if you want, check the introduction to Event handler in GrandNode.

As you can see in the example, in the order messages you will be able to use new order number token. It will be a YEAR/NUMBER. For example 2020/3.

This example you will find out of the box in GrandNode. Just go to Grand.Web -> Roslyn -> CodeFile.csx.

More than just an e-commerce platform. GrandNode is the most advanced e-commerce platform at the market. Unbelievable? See for yourself!