Snipplet, include y embed

Usando Twig podemos incluir snipplets, que son los archivos “.tpl” dentro de la carpeta con su mismo nombre, de tres formas.

Estas nos dan la posibilidad de reutilizar un mismo componente en más de un lugar, optimizando el código y facilitando su mantenimiento. 

Podemos incluir snipplets:

  • Usando {%  snipplet %}
  • Usando {%  include %}
  • Usando {%  embed %}

{% snipplet %}

Si sólo necesitamos llamar a un snipplet sin ningún tipo de parámetro extra o condicional, entonces esta manera es la indicada.

{% if settings.ad_bar and settings.ad_text %}
    {% snipplet "header/header-advertising.tpl" %}
{% endif %}

En el ejemplo simplemente llamamos al snipplet header-advertising.tpl dentro de carpeta header, solo para mostrar un mensaje arriba de la navegación. 

Como estamos usando {% snipplet %} no es necesario aclarar que la carpeta header está dentro de la carpeta snipplets

{% include %}

Include es una forma un poco más compleja de llamar snipplets donde podemos usar ciertas condiciones dentro del propio snipplet a la hora de usarlo, vamos a ver un ejemplo.

En el theme Base tenemos un snipplet llamado notifications.tpl donde tenemos el HTML para todas las notificaciones del Storefront, que hasta ahora son dos:

Agregar al carrito:

Ver y seguir el detalle de la compra:

Dentro del snipplet tenemos algo similar a esto:

{# Order notification #}

{% if order_notification %}
    content
{% endif %}

{# Add to cart notification #}

{% if add_to_cart %}
    content
{% endif %}

Podemos ver dos condiciones, una para mostrar el mensaje de “seguí tu orden” con la condición “order_notification”; y otra para el mensaje de “producto agregado al carrito” con la condición “add_to_cart”.

Ambas condiciones son personalizadas, es decir que son nombres que definimos de forma arbitraria y no dependen de un cambio en el PHP.

Estas condiciones las usamos a la hora de llamar al archivo notification.tpl para elegir qué notificación queremos mostrar. 

Por ejemplo si queremos mostrar ambas en el mismo lugar (hablando en términos de posición en el HTML) hacemos lo siguiente:

{% include "snipplets/notification.tpl" with {order_notification: true, add_to_cart: true} %}

O si necesitamos mostrar una notificación en un lugar y otra en otro, podemos hacerlo llamando al mismo snipplet en lugares distintos:

{% include "snipplets/notification.tpl" with {order_notification: true} %}
...
{% include "snipplets/notification.tpl" with {add_to_cart: true} %}

En el ejemplo primero mostramos sólo la notificación de “seguí tu orden” y debajo usamos el mismo snipplet solo para mostrar la notificación de “producto agregado al carrito”.

Otro caso donde usamos include es para mostrar todos los iconos SVG del theme. 

Nuestros iconos se encuentran en archivos “.tpl” que contienen un código SVG como cualquier otro: 

<svg class="{{ svg_custom_class }}" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M508.5 468.9L387.1 347.5c-2.3-2.3-5.3-3.5-8.5-3.5h-13.2c31.5-36.5 50.6-84 50.6-136C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c52 0 99.5-19.1 136-50.6v13.2c0 3.2 1.3 6.2 3.5 8.5l121.4 121.4c4.7 4.7 12.3 4.7 17 0l22.6-22.6c4.7-4.7 4.7-12.3 0-17zM208 368c-88.4 0-160-71.6-160-160S119.6 48 208 48s160 71.6 160 160-71.6 160-160 160z"/></svg>

Solo que dejamos la clase de CSS dentro de un parámetro con Twig:

<svg class="{{ svg_custom_class }}"

Hacemos esto para elegir la clase que necesitemos a la hora de usar el include, reemplazando lo que está entre llaves, por lo que querramos:

{% include "snipplets/svg/search.tpl" with {svg_custom_class: "icon-inline svg-icon-text"} %}

Para cerrar, es importante remarcar que necesitamos especificar que el archivo está en la carpeta “snipplets”, a diferencia de cuando usamos {% snipplet %} donde no es necesario.

{% embed %}

Por último los “embeds”, son muy similares a un include solo que un poco más complejo. Vamos a verlo con un ejemplo:

En el theme Base usamos el embed para el componente “select” (entre muchos otros).

El snipplet concreto es form-select.tpl y su código es el siguiente:

<div class="form-group {{ select_group_custom_class }}">
    {% if select_label %}
        <label {% if select_label_id%}id="{{ select_label_id }}"{% endif %} class="form-label {{ select_label_custom_class }}" {% if select_for %}for="{{ select_for }}"{% endif %}>{{ select_label_name }}</label>
    {% endif %}
    <select 
        {% if select_id %}id="{{ select_id }}"{% endif %}
        class="form-select {{ select_custom_class }} {% if select_inline %}form-control-inline{% endif %}"
        {% if select_name %}name="{{ select_name }}"{% endif %}>
        {% block select_options %}
        {% endblock select_options %}
    </select>
    <div class="form-select-icon">
        {% include "snipplets/svg/chevron-down.tpl" with {svg_custom_class: "icon-inline icon-w-14 icon-lg svg-icon-text"} %}
    </div>
</div>

En este archivo usamos clases a las cuales le pasamos parámetros cuando sean incluidas (como en el ejemplo de los SVGs con un include).

<div class="form-group {{ select_group_custom_class }}">

También usamos condiciones personalizadas como en los include.

{% if select_label %}
    <label {% if select_label_id%}id="{{ select_label_id }}"{% endif %} class="form-label {{ select_label_custom_class }}" {% if select_for %}for="{{ select_for }}"{% endif %}>{{ select_label_name }}</label>
{% endif %}

Pero la diferencia con un include está en el uso de un “block”

<select 
    {% if select_id %}id="{{ select_id }}"{% endif %}
    class="form-select {{ select_custom_class }} {% if select_inline %}form-control-inline{% endif %}"
    {% if select_name %}name="{{ select_name }}"{% endif %}>
    {% block select_options %}
    
    {% endblock select_options %}
</select>

El block permite dejar un espacio vacío al cual podremos agregarle el contenido que necesitemos a la hora de usar el snipplet. 

En el caso del select, lo usamos para luego agregar las opciones que necesitemos ya que pueden ser dos o más.

Un ejemplo de como llamar al form-select.tpl usando embed es el siguiente

{% embed "snipplets/forms/form-select.tpl" with{select_label: false, select_custom_class: 'js-sort-by'} %}
    {% block select_options %}
        <option value="1">{{ 'texto 1' | translate }}</option>
        <option value="2">{{ 'texto 2' | translate }}</option>
        <option value="3">{{ 'texto 3' | translate }}</option>
        <option value="4">{{ 'texto 4' | translate }}</option>
    {% endblock select_options%}
{% endembed %}

En esta caso mostramos un select sin un label (ya que tiene la condición select_label: false) con una clase personalizada “js-sort-by” y dentro del block tiene cuatro opciones estáticas.

Otro ejemplo es del componente para las variantes de un producto

{% for variation in product.variations %}
    <div class="col-12">
        {% embed "snipplets/forms/form-select.tpl" with{select_label: true, select_label_name: '' ~ variation.name ~ '', select_for: 'variation_' ~ loop.index , select_id: 'variation_' ~ loop.index, select_name: 'variation' ~ '[' ~ variation.id ~ ']', select_custom_class: 'js-variation-option js-refresh-installment-data'} %}
            {% block select_options %}
                {% for option in variation.options %}
                    <option value="{{ option.id }}" {% if product.default_options[variation.id] == option.id %}selected="selected"{% endif %}>{{ option.name }}</option>
                {% endfor %}
            {% endblock select_options%}
        {% endembed %}
    </div>
{% endfor %}

Acá hay muchas más propiedades pero la mayor diferencia está dentro del block donde ya no tenemos contenido estático sino que usamos un for para iterar sobre las propiedades de cada variante.

Para cerrar debemos aclarar que el snipplet está dentro de la carpeta snipplets, de la misma forma que lo hacemos en un include.

Tip: 

Si necesitamos usar texto estático en un embed (y en un include también) podemos hacerlo de la siguiente forma

select_label_name: 'text' | translate,

Mientras que si queremos un texto dinámico vamos a tener que hacerlo de esta otra

select_label_name: '' ~ variation.name ~ '',