NAV Navbar
HTML/JS Liquid + HTML
  • Bienvenida
  • Liquid 101
  • Variables globales y locales
  • Estructura de plantillas
  • Bloques html custom
  • Etiquetas Bootic
  • Filtros Liquid
  • Archivos e imágenes
  • Buscador de productos
  • Bootic.js y los módulos Javascript
  • Ejemplos y trucos
  • Bienvenida

    ¡Buenas!

    Éste es nuestro nuevo sitio para desarrolladores. Aquí encontrarás toda la documentación necesaria para construir, modificar y extender las tiendas que corren sobre nuestra plataforma.

    Y si no lo encuentras, ¡lo inventamos! (En serio, si alguna etiqueta o método te ayudaría a hacer la pega mejor o más rápido, avísanos y lo vemos).

    Sin contar esta parte, este sitio se divide las siguientes secciones o capítulos:

    1. Liquid 101 (introducción a Liquid)
    2. Variables globales y locales
    3. Estructura de plantillas
    4. Bloques html custom
    5. Etiquetas Bootic
    6. Archivos e imágenes de apoyo (assets)
    7. Bootic.js y los módulos Javascript
    8. Ejemplos y trucos

    Como ves, el sitio funciona con tres columnas, por lo que normalmente verás ejemplos en código a la derecha de lo que estés leyendo aquí; en esta columna. A la izquierda hay un menú con todas las secciones de este sitio (que es básicamente una sola gran página) y con un búscador AJAX bien rápido para ayudarte a encontrar lo que busques.

    Primeros pasos

    Cada tienda en Bootic funciona en base a un conjunto de plantillas, también llamados "temas", al igual que lo hace Wordpress u otros gestores de contenido.

    Un tema de diseño es, en esencia, un conjunto de plantillas HTML acompañadas de imágenes y hojas de estilo (CSS), opcionalmente con archivos de apoyo (assets) como librerías Javascript, tipografías, íconos (SVG), y cómo no, canciones MIDI.

    Ofrecemos varios temas para elegir, que puedes usar a modo de ejemplo o bien como punto de partida para crear tus propios diseños.

    Cambiando temas

    Por defecto, las tiendas nuevas en Bootic utilizan nuestro tema Oxygen, que posee un diseño simple y limpio y por lo tanto funciona muy bien para diversos tipos de tiendas (aquí puedes ver Oxygen en acción). Si quieres probar otro diseño, puedes entrar a la seccion Diseño del panel de administración Bootic y elegir otro.

    Una vez cambiado, basta con que abras o refresques la portada de tu tienda y verás cómo se ve con el nuevo diseño. Algunos temas ofrecen algunas opciones básicas como por ejemplo la cantidad de elementos por fila o si deseas que se muestre el nivel de stock de los productos en catálogo.

    Modificando las plantillas

    Para modificar una plantilla puedes hacerlo usando el editor de plantillas que está disponible en el panel de administración, dentro del menú Diseño.

    Bootic code editor

    Ahora, si prefieres trabajar en forma local con Sublime Text, Vim, Atom o tu editor favorito también puedes hacerlo. Para eso deberás usar el comando bootic themes que es parte de nuestro Bootic CLI.

    Para empezar a modificar el código, es necesario que entiendas cómo funciona nuestro sistema de plantillas, empezando por la sintaxis que ellas utilizan.

    Liquid 101

    {% for product in products %}
    <div class="product">
      <h3><a href="{{ product.url }}">{{ product.model }}</a></h3>
      <span class="price">{{ product.price }}</span>
    </div>
    {% endfor %}
    

    Bootic usa Liquid como lenguaje para crear y modificar las plantillas que generan el HTML de una tienda. Al igual que otros sistemas de templating, Liquid permite combinar código HTML con etiquetas especiales para desplegar el contenido.

    Por ejemplo, para mostrar una lista de productos en la plantilla products.html, usaríamos el código que ves a la derecha.

    La sintaxis de Liquid define tres tipos de elementos: variables, filtros y etiquetas. Pero vamos por parte (dijo el forense).

    Variables y objetos

    Una variable es, esencialmente, una expresión que representa un valor, que puede ser:

    En Liquid, las variables se rodean con {{ y }} e insertan el valor en el HTML generado:

    Bienvenido a <strong>{{ shop.name }}</strong>!
    

    Resulta en:

    Bienvenido a <strong>Mi tienda</strong>!
    

    Al primer tipo de variable se le puede aplicar los filtros de texto, que a su vez tienen como subconjunto los filtros de URLs y los filtros de imágenes (ambos cadenas de texto, pero de un tipo especial).

    Pero en caso de que la variable represente un conjunto de elementos, necesitamos poder iterar sobre ellos para entonces usar los bigotes sobre los atributos que queramos usar o desplegar. Para esto usamos la etiqueta for:

    <!-- Iterando sobre un conjunto de fabricantes (vendor) -->
    {% for vendor in shop.vendors %}
      {{ vendor.url }}
    {% endfor %}
    

    A las variables que corresponden a conjunto de elementos, nos referimos como variables iterables. A ellas también se les puede aplicar filtros, pero aquellos que se aplican sobre éste tipo particular de variables: los filtros de conjunto.

    Por último, dependiendo de la plantilla que estés editando, tendrás distintas variables disponibles para usar. Algunas variables están disponibles para todas las plantillas, ya que son globales (como por ejemplo shop, que es algo que no cambia dependiendo de la página) mientras otras son locales.

    Más adelante entramos al detalle de las variables globales versus las locales.

    Filtros: ¿Para qué sirven y cómo se usan?

    {{ 'Esta es una frase muy recontra requete larga.' | truncate: 20 }}
    

    Resulta en:

    Esta es una frase mu...
    

    Un filtro modifica el valor contenido en una variable o expresión. La sintáxis de un filtro es expresión | filtro. La barra vertical quiere decir "toma el valor de la expresión y pásalo a través del filtro".

    Los filtros se pueden encadenar:

    {{ 'estilos.css' | asset_url | stylesheet_tag }}
    

    Lo de arriba genera:

    <link rel="stylesheet" src="/themes/1234/assets/estilos.css" />
    

    Dependiendo de cuán complejo sea lo que quieres hacer, puede que necesites usar filtros o puede que no. Nosotros en general los usamos para cosas bien específicas, como por ejemplo para linkear hacia los archivos CSS y JS de una tienda.

    A la derecha puedes ver esto en acción. Lo que estamos haciendo aquí es pasar la cadena 'estilos.css' al filtro asset_url, que lo que hace es buscar la ruta hacia los archivos de apoyo de tu tienda. Este filtro retorna esa ruta y la agrega como prefijo a la cadena original -- algo así como '/themes/1234/assets/estilos.css'.

    Por último, esa cadena resultante de asset_url es pasada al filtro stylesheet_tag, que inserta la cadena dentro de un <link rel="stylesheet"> de forma de generar el <link> completo.

    ¿Fácil, no?

    Para ponerte a prueba, a ver si logras decifrar cómo construirías el siguiente pedazo de HTML:

      <img src="/themes/1234/assets/imagen.jpg" alt="Una imagen" />
    

    ¿Se te ocurre cómo? Piénsalo bien, guiándote por el ejemplo que vimos recién. Cuando llegues a una solución apreta aquí para ver si tenías razón!

    Más adelante encontrarás una lista con todos los filtros disponibles para jugar.

    {% Etiquetas Liquid %}

    {% if product.available_stock > 10 %}
      Tenemos cachá de este producto.
    {% elsif product.available_stock <= 0 %}
      No tenemos ná.
    {% else %}
      Quedan pocos, así que apúrala oh!
    {% endfor %}
    

    Las etiquetas Liquid por lo general sirven para agregar lógica condicional a tu código, como ves en el súper ejemplo de la derecha. También sirven para poder trabajar con conjuntos de elementos (como el ejemplo de for vendor in shop.vendors arriba), y por último, para llamar a métodos propios de Bootic que retornan un valor producto de un cálculo o lógica específica.

    Es decir, las etiquetas Liquid (que se rodean con {% y %}, a diferencia de las variables y filtros) se usan para todo lo que no sea insertar o escupir el valor de una variable de texto o numérica.

    En la siguiente sección verás qué etiquetas Liquid tienes disponible en tu arsenal. El lenguaje Liquid proporciona la gran mayoría, pero Bootic también incorpora algunas para ayudarte a hacer cosas con menos código. Probablemente la más importante de todas estas etiquetas es {% add_to_cart %}, que la usamos para generar el HTML del botón "Agregar al carro".

    Las etiquetas, a diferencia de las variables, no aceptan el uso de filtros, y si bien todas se encierran con bigotes ({% y %}), cada una acepta opciones específicas que veremos en la próxima sección.

    Variables globales y locales

    <p>Bienvenido a {{ shop.name }}!</p>
    

    En el sistema de plantillas de Bootic, hay ciertas variables que son "globales", es decir, están disponibles para usarse en cualquier plantilla.

    Una variable global, por ejemplo, es shop, que contiene un objeto con la información asociada a la tienda -- como su nombre y URL -- y por lo tanto es muy útil tenerla a mano en cualquier plantilla, ya sea un formulario de contacto (form.html) o bien, la portada del sitio (home.html).

    Sin embargo, existen otras variables que dependen del contexto y que sólo están disponibles en algunas plantillas. A éstas nos referimos como variables locales.

    Por ejemplo, en la plantilla form.html existe una variable local llamada form que corresponde al objeto asociado al formulario desplegado, pero en esa plantilla no existe una variable product ni tampoco collection.

    La razón es simple: al mostrarse el detalle de un producto, Bootic sabe cuál producto es el que está mirando el usuario y por tanto asocia ése producto específico a la variable product. Por el contrario, al visitar un formulario de contacto, al no estar mostrándose un producto específico Bootic no sabría qué producto asociar a la variable product.

    Listado de variables globales

    Generales:

    Menús, links y galerías:

    Productos y sus agrupaciones:

    Contenido estático:

    La variable resource

    resource es una variable que verás mucho en las plantillas Bootic, y la usamos porque está presente en todas las páginas donde hay un recurso principal: en la ficha de un producto sería el producto desplegado, o en el caso de un listado de productos sería la colección (en caso de estar filtrando productos por colección, obviamente).

    Esto la hace una herramienta muy práctica para poder hacer chequeos a nivel global, o simplemente para insertar una variable en común.

    Por ejemplo, como todos los objetos que puede contener resource tienen la variable name, puedes hacer:

    <!-- en layout.html -->
    {% if resource %}
      <h1>Estás viendo nuestra {% resource.name %}</h1>
    {% endif %}
    

    Lo cual en algunos casos dirá:

    <h1>Estás viendo nuestra Bicicleta Azul</h1>
    

    Pero también:

    <h1>Estás viendo nuestra Colección Montaña</h1>
    

    slug es otra de las variables que contienen todos los objetos que pueden ser un resource, por lo que puedes puedes hacer chequeos de este tipo:

    {% if resource.slug == 'una-cosa-importante' %}
      Esta es una cosa muy importante!
    {% endif %}
    

    O bien, puedes comparar directamente el objeto asignado a la variable resource con otras variables que puedan estar presentes en una vista:

    {% if resource == product_type %}
      Estás mirando los productos de tipo {{ product_type.name }}.
    {% elsif resource == collection %}
      Estás mirando la colección {{ collection.name }}
    {% endif %}
    

    El listado de variables comunes que poseen todos los posibles resource son:

    El objeto shop

    Disponible a través de la variable global shop.

    El objeto menu

    Disponible a través de la variable global menus, ya sea iterando o usando un handle (ej. menus.sidebar).

    Disponible al iterar sobre el atributo links de un objeto menu.

    También responde a los siguientes métodos, dependiendo del tipo de objeto que esté asociado al link.

    {% for item in galleries.portada.items %}
      <div>
        <img src="{{ item.image | resize:'540x' }}" />
      </div>
    {% endfor %}
    

    El objeto collection

    Disponible en la plantilla collection.html o a través de la variable global collections, ya sea iterando o bien usando una manilla (ej. collections.ofertas.products).

    El objeto product_type

    Disponible iterando sobre la variable global product_types o como el atributo de un producto.

    El objeto vendor

    Disponible al iterar sobre el conjunto global vendors, o como el atributo vendor de un producto.

    El objeto tag

    El objeto product

    Disponible en la plantilla product.html o dentro de un loop de productos.

    Conjuntos o objectos relacionados:

    Variantes:

    Precios:

    Imágenes asociadas:

    Archivos asociados:

    Tipo y atributos:

    Productos similares o relacionados:

    El objeto variant

    Disponible al iterar sobre el conjunto variants un producto.

    Variables relacionadas al stock:

    Variables relacionadas al precio:

    El objeto bundle

    Un bundle es un pack, o grupo de productos. Un pack no tiene SKU y su stock está alimentado por el stock de los productos que lo componen.

    El objeto asset

    Contiene las siguientes variables:

    Variables específicas para imágenes:

    El objeto page

    Disponible en la plantilla page.html. o a través de la variable global pages, ya sea iterando o bien usando un handle específico (ej pages.bienvenida.body).

    El objeto form

    Disponible en la plantilla form.html o a través de la variable global forms, ya sea iterando o bien usando una manilla (ej. forms.landing.body).

    Los campos de un formulario de contacto se despliegan usando la etiqueta especial {% contact_form_fields %}, como aparece en la documentación de la plantilla form.html.

    El objeto blog

    Objeto global. Tiene las siguientes variables:

    El objeto post

    Disponible en la plantilla post.html o al iterar sobre un conjunto de posts (blog.posts). Tiene las siguientes variables:

    Estructura de plantillas

    Plantilla Rutas donde se usa (ejemplo) Variables locales
    layout.html Todas
    home.html /
    products.html /products, /types/discos, /collections/ofertas, /vendors/adidas, etc products, resource, y collection, product_type, vendor y/o tag, en caso que aplique
    product.html /products/ipod-nano product, resource
    page.html /pages/about-us page, resource
    form.html /forms/contacto form, resource
    blog.html /blog blog (global)
    post.html /blog/2014/01/13/hola-mundo post, resource
    cart.html /cart cart, related_products_to_just_added

    layout.html

    Esta es la plantilla principal de tu tienda. Cada página que visitas se despliega dentro de layout.html usando la etiqueta {{ content_for_layout }}.

    Aquí hay un layout.html super hiper simplificado:

    <html>
      <head>
        <title>{% current_title %} - {{ shop.name }}</title>
        <meta name="description" content="{% current_description %}">
      </head>
      <body>
        {{ content_for_layout }}
      </body>
    </html>
    

    Si tuviéramos una plantilla home.html con lo siguiente:

    <h1>¡Bienvenido a nuestro sitio!</h1>
    

    Y visitamos la portada, {{ content_for_layout }} es reemplazada automáticamente por el contenido de home.html. El resultado sería:

    <html>
      <head>
        <title>Calzones rotos a domicilio - Mi tienda</title>
        <meta name="description" content="Calzones rotos a domicilio">
      </head>
      <body>
        <h1>¡Bienvenido a nuestro sitio!</h1>
      </body>
    </html>
    

    Incluyendo otras plantillas

    Para evitar duplicar código, a veces es buena idea poner fragmentos de HTML que se repiten mucho en "sub-plantillas" e incluirlas donde se usen.

    Para esto existe la etiqueta Liquid include, que permite inyectar o meter el código de una plantilla dentro de otra.

    {% include 'mi_plantilla' %}
    

    Por ejemplo, uno de los temas de Bootic usa esta técnica para incluir distintas barras laterales dependiendo de la página que se está desplegando:

    {% if template == 'page' %}
      {% include 'sidebar_pages' %}
    {% elsif template == 'blog' %}
      {% include 'sidebar_blog' %}
    {% else %}
      {% include 'sidebar' %}
    {% endif %}
    

    home.html

    Esta plantilla se carga al visitar la portada de tu tienda Bootic, y su contenido se inserta donde tengas puesta la etiqueta {{ content_for_layout }} de tu plantilla principal, layout.html.

    La portada de tu sitio es el lugar ideal para mostrar productos destacados, ofertas, noticias recientes, "banners" y otros elementos que quieras poner en vitrina.

    Dado que la portada puede contener cosas distintas dependiendo del rubro y estilo de tu tienda, es recomendable que revises la plantilla home.html del diseño defecto de tu tienda Bootic.

    En la parte final de este sitio puedes ver algunos ejemplos de cosas puedes hacer en tu home.html:

    products.html

    {% paginate products by 5 %}
      {% if paginated_list.size == 0 %}
        <p>No hay productos</p>
      {% else %}
        {% for product in paginated_list %}
          <div ciass="product">
            <a href="{{ product.url }}" class="image">
              {% product.first_image.small | image_tag %}
            </a>
    
            <h3>
              <span class="vendor">{{ product.vendor }}</span>
              <a href="{{ product.url }}">{{ product.model }}</a>
            </h3>
    
            {% if product.price.positive %}
              <span class="price">{{ product.price }}</span>
            {% endif %}
          </div>
        {% endfor %}
    
        {{ paginated_list | pagination }}
      {% endif %}
    {% endpaginate %}
    

    Esta es la plantilla usada por Bootic para mostrar listas y colecciones de productos. Esta se usa para desplegar listas de productos, por lo que tiene una variable llamada products que contiene el conjunto de productos a mostrar.

    Colecciones, Tipos, Fabricantes y Tags

    Este conjunto de productos se obtiene en forma dinámica, ya que Bootic permite navegar por tu catálogo usando distintos criterios. Estos son:

    Además de esto, Bootic permite combinar dos de cualquiera los cuatro criterios (colección, tipo, fabricante y tag) en caso de que quieras ofrecer niveles de navegación más específicos. Por ejemplo:

    Paginando

    Normalmente querrás usar paginación para mostrar tus productos en varias páginas, ya que esa lista podría tener muchos productos (por ejemplo en el caso de /products, que muestra todos los productos visibles de tu tienda).

    En el ejemplo de la derecha iteramos sobre el conjunto de productos contenidos en la variable products y generamos un bloque div para cada producto, mostrando detalles básicos de cada uno como título, imagen pequeña, precio y fabricante.

    product.html

    Esta plantilla representa a la ficha de un sólo producto en tu tienda. Los datos e imágenes de cada producto los editas en la sección "productos" de la interfaz de administración de tu tienda.

    En la plantilla product.html tienes un objeto product con atributos que puedes usar (ver listado completo de atributos en variables/product).

    También existe la etiqueta especial add_to_cart que genera el html del botón para agregar al carro de compra.

    Mostrar el precio de un producto

    La función format muestra el precio formateado de acuerdo a moneda actual de la tienda.

    Precio: {{ product.price.format }}
    

    Precio y precio de comparación de producto

    Todo producto en lista o página de detalles tiene un atributo price con el precio del producto. Mostrarlo en plantillas es sencillo:

    Precio: {{ product.price }}
    

    Si no quieres mostrar el precio en caso de que sea cero, puedes revisar la variable positive del objeto price de un producto.

    {% if product.price.positive %}
      Precio: {{ product.price }}
    {% endif %}
    

    Esto también sirve en caso de que quieras evitar que un producto se pueda comprar, combinándolo con la etiqueta {% add_to_cart %}:

    {% if product.price.positive %}
      {% add_to_cart %}
    {% endif %}
    

    Además los productos pueden tener un campo opcional de precio de comparación. Muy útil si el producto está en oferta y quieres mostrar el precio anterior. En el panel de administración:

    precio de comparación de productos

    En las plantillas de producto (product.html para el detalle, products.html u otros para las listas) puedes mostrar el precio de comparación como prefieras, si es que está disponible:

    {% if product.has_price_comparison %}
      Precio anterior: {{ product.price_comparison.format }}
    {% endif %}
    

    También puedes mostrar el porcentaje de descuento del precio actual con respecto al an "anterior" o comparación:

    {% if product.has_price_comparison %}
      Descuento: %{{ product.price_comparison_percentage }}
    {% endif %}
    

    Variantes y botón de compra

    Agregar al carro de compras

    La etiqueta {% add_to_cart %} genera un formulario HTML prediseñado con la lista de variantes del producto, un campo para ingresar la cantidad de unidades y un botón para guardar la variante y cantidad elegida en el carro de compras.

    <div id="agregar_al_carro">
      {% add_to_cart %}
    </div>
    

    La etiqueta también revisa el estado de cada variante y deshabilita las que esten fuera de stock. Normalmente esta etiqueta es suficiente para la mayoría de los casos. Con un poco de CSS puedes cambiar el estilo de los elementos generados.

    Colecciones

    Puedes usar product.collections para listar las colecciones a las que el producto pertenece.

    {% if product.has_collections %}
      <h3>Colecciones de {{ product.model }}</h3>
      <ul>
        {% for coll in product.collections %}
        <li>
          <a href="{{ coll.url }}">{{ coll.name }}</a>
        </li>
        {% endfor %}
      </ul>
    {% endif %}
    

    También puedes acceder a una colección en particular usando su "manilla" o título normalizado:

    {% if product.collections.catalogo %}
      Este producto está en la colección {{ product.collections.catalogo.name }}!
    {% endif %}
    

    Etiquetas

    Al igual que con las colecciones, puedes usar product.tags para listar las etiquetas a las que el producto pertenece.

    {% if product.has_tags %}
      <h3>Etiquetas de {{ product.model }}</h3>
      <ul>
        {% for tag in product.tags %}
        <li>
          <a href="{{ tag.url }}">{{ tag.name }}</a>
        </li>
        {% endfor %}
      </ul>
    {% endif %}
    

    Al igual que con las colecciones, también puedes acceder a una etiqueta en particular usando su "manilla"* o nombre normalizado:

    {% if product.tags.hecho_en_chile %}
      Este producto es hecho in Chile!
    {% endif %}
    

    Mostrando imágenes de un producto

    {% if product.has_images %}
      {% for image in product.images limit:2 %}
        <div class="product-image">
          <a href="{{ image.large }}">
            {% image.medium | image_tag %}
          </a>
        </div>
      {% endfor %}
    {% endif %}
    

    La variable product.images contiene un conjunto con las imágenes asociadas a un producto, en el orden definido en el panel de administración. Son objetos de tipo asset.

    Otros archivos

    También puedes subir y asociar otro tipo de archivos a los productos (por ejemplo, una catálogo en formato PDF).

    {% if product.has_files %}
      {% for file in product.files %}
        <a href="{{ file.url }}" class="file-{{ file.extension }}">
          {{ file.file_name }}
        </a>
      {% endfor %}
    {% endif %}
    

    Para mostrarlos, puedes iterar sobre el product.files, que contiene el conjunto de archivos asociados a un producto (no imágenes). Si bien los archivos en sí no son imágenes, los objetos también son de tipo asset, por lo que contienen las mismas variables (url, extension, file_name, etc).

    page.html

    <h2>{{ page.name }}</h2>
    
    <div class="entry">
      {{ page.body }}
    </div>
    

    Esta es una de las plantillas más sencillas de Bootic y su función es mostrar el contenido de una página editable de Bootic. Esta plantilla es usada cuando ves una página en URLs del tipo /pages/sobre-la-empresa o /pages/terminos-y-condiciones

    Los atributos disponibles para la variable page los puedes ver aquí.

    form.html

    <h2>{{ form.name }}</h2>
    
    {% if form.sent %}
    
      <p>¡Recibimos tu mensaje! Gracias.</p>
    
    {% else %}
    
      <div class="entry">
        {{ form.body }}
      </div>
    
      {% contact_form_fields %}
    
    {% endif %}
    

    Esta plantilla despliega los formularios de contacto que puedes crear en tu tienda Bootic.

    blog.html

    Esta plantilla está diseñada para mostrar las noticias que publiques en tu tienda.

    Administración de blog y noticias en Bootic

    Paginando artículos y "leer más"

    {% paginate blog.posts by 10 %}
      {% for post in paginated_list %}
        <div class="post">
          <h3>
            <a href="{{ post.url }}">{{ post.title }}</a>
          </h3>
    
          <div class="entry">
            {{ post.body | split_text }}
          </div>
    
          <span class="date">
            Publicado el {{ post.published_on | date:'%d/%m/%Y' }}
          </span>
        </div>
      {% endfor %}
    
      <!-- no olvidar la paginacion! -->
      {{ paginated_list | pagination }}
    {% endpaginate %}
    

    En el ejemplo de la derecha usamos la etiqueta paginate para generar páginas de 10 artículos cada uno. Además, usamos el filtro split_text para generar un extracto de los artículos, y el filtro date para darle formato a la fecha de cada uno.

    El filtro split_text, aplicado a un artículo, muestra la primera parte del artículo si detecta un símbolo especial insertado en él. En caso de no encontrar el símbolo, se hará un corte arbitrario luego de las primeras 10 líneas de texto.

    El botón para introducir "el corte" en forma manual es el siguiente:

    Dividir artículo

    Y así es cómo se ve el resultado final:

    Leer más

    post.html

    <div class="post">
      <h2>{{ post.title }}</h2>
    
      <div class="entry">
        {{ post.body }}
      </div>
    
      <p class="date" title="{{ post.published_on | date:'%b %d %H:%M:%S %Y' }}">
        Publicado el <strong>{{ post.published_on | date:'%d/%m/%Y' }}</strong> por {{ post.user_name }}
      </p>
    </div>
    

    La plantilla post.html muestra el detalle de cada noticia y tiene su propia URL. por ejemplo /blog/2024/03/18/mi-primera-noticia. En esta plantilla puedes mostrar el artículo completo.

    cart.html

    Ejemplo de una plantilla cart.html:

    {% if cart.units > 0 %}
    
      {% dynamic_cart_table %}
    
    {% else %}
    
      <h2>Carro de compras</h2>
      <p>Tu carro está vacío. ¡Sigue participando!</p>
    
    {% endif %}
    

    La plantilla cart.html contiene el detalle de los productos que el usuario ha ingresado al carro de compra por efecto de cliquear el botón "Agregar al carro".

    Si la cantidad de unidades (cart.units) es mayor a cero mostrará una tabla con 5 columnas - Figura 1:

    1. Foto y nombre del producto
    2. Precio unitario
    3. Texto de ingreso con la cantidad de productos
    4. Subtotal del precio unitario por la cantidad de productos
    5. Enlace para borrar la fila

    Carro de compra

    Además mostrará 3 botones:

    1. Seguir comprando (volverá al portada de la tienda)
    2. Comprar (irá a la página de checkout)

    Si la cantidad de productos es 0 (el carro está sin productos) mostrará lo siguiente:

    Bloques html custom

    En Bootic tenemos un sistema de edición de plantillas intuitivo y amiagle que con unos cuantos clicks puedes agregar galerias, secciones, imagenes, etc.

    En este modo no se tiene acceso a la edición de las plantillas html como product.html, product_item.html, home.html, etc. Entonces si necesitas agregar alguna funcionalidad extra o libreria js, aunque probablemente ya tenemos un componente que solucione tu problema, puedes insertar bloques html en layout.html.

    layout.html tiene la siguiente forma(el ejemplo esta un poco simplificado).

    <!doctype html>
    <html lang="es">
    <head>
      <title>{% current_title %} - {{ shop.name }}</title>
      {% bootic_header 'v2' %}<!-- carga bootic.css y shops.js -->
    
      {% include_optional 'custom_head.html' %}
      {% include_optional 'custom.css' %} <!-- Este archivo viene creado por defecto -->
    </head>
    <body>
      {% render_header %}<!-- carga before_header.html y after_header.html en caso de que existan -->
      {% render_content %}<!-- carga before_content.html y after_content.html en caso de que existan -->
      {% render_footer %}<!-- carga before_footer.html y after_footer.html en caso de que existan -->
    
      {% include_optional 'custom_foot.html' %}
    </body>
    </html>
    

    Para insertar html al layout solo bastaria con crear el archivo que corresponde al lugar del layout donde necesites agregar código.

    Si por ejemplo se necesita agregar algun css al layout la manera de lograrlo es crear un archivo custom_head.html y dentro de este archivo importar el css.

    <link href="https://some_web.com/foo.css" rel="stylesheet">
    

    O si necesitas agregar alguna libreria js e incluirla al final del layout puedes crear el archivo custom_foot.html e incluir la libreria de la siguiente forma.

    <script type="text/javascript" src="https://some_web.com/foo.js"></script>
    

    Etiquetas Bootic

    Para propósitos de esta documentación, no entraremos en el detalle de las etiquetas Liquid que son parte del lenguaje:

    Si quieres leer más sobre ellas, te recomendamos visitar la documentación oficial de Liquid.

    Etiquetas Bootic generales:

    Etiquetas Bootic para carro y productos:

    Abajo, la descripción y ejemplos de cada una.

    {{ content_for_layout }}

    Inserta el contenido generado por la plantilla correspondiente a la página desplegada (home.html en la portada, cart.html en /cart, etc).

    <!-- layout.html -->
    <html>
    <head>
      <title>{% current_title %}</title>
    </head>
    
    <body>
    {{ content_for_layout }}
    </body>
    </html>
    

    {% current_title %}

    Genera un título para ser usado en la etiqueta <title>, dependiendo de la página desplegada.

    <head>
      <meta charset="utf-8" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>{% current_title %} - {{ shop.name }}</title>
    

    {% current_description %}

    Genera una descripción corta para usar en el meta description del encabezado. El contenido generado dependerá de la página desplegada.

    <head>
      <meta charset="utf-8" />
      <meta name="description" content="{% current_description %}" />
      (...)
    

    {% navigation_path %}

    Genera la ruta de navegación (también llamados "breadcrumbs") para la página desplegada. Muy práctico, y comúnmente usado en layout.html.

    <div class="breadcrumbs">
      {% navigation_path %}
    </div>
    

    {% bootic_header [version] %}

    Incluye los archivos CSS compartidos por todas las tiendas Bootic (como el carro, los formularios, y el botón agregar al carro), y dependiendo de la página, metainformación asociada: el link RSS en las páginas del blog, y en las fichas de productos datos estructurados (LD+JSON) junto con las etiquetas para el OpenGraph de Facebook.

      {% bootic_header 'v2' %}
    

    Como alternativa, puedes usar stripped_bootic_header en vez de bootic_header con lo cual el código generado no incluirá los estilos predefinidos de Bootic. Esto es útil en caso de que quieras tener el control absoluto de los estilos de tu tienda, sin que haya otro CSS entre medio revolviendo el gallinero.

    Incluye los archivos JS compartidos por todas las tiendas Bootic, y que son requeridos para gran parte de la funcionalidad asociada a la elección de variantes y el carro.

      {% bootic_footer 'v2' %}
    

    {% loop [objects] in 'template' %}

    Permite iterar sobre un conjunto de objetos o variables dentro de una (sub) plantilla. Veamos un caso de uso práctico.

    En la mayoría de los casos, el HTML utilizado para mostrar un producto dentro de un listado (en products.html, o los destacados en home.html o los similares en product.html) es el mismo.

    Para esto es muy conveniente utilizar una misma plantilla e incluirla en cada listado utilizando la etiqueta loop.

    Plantilla reutilizable product_item.html

    Por ejemplo, podríamos crear una plantilla HTML llamada product_item.html con el siguiente código:

    <!-- product_item.html -->
    <div class="product item-{{ product_item_counter }}">
      <a href="{{ product_item.url }}" class="product-image">
        <img src="{{ product_item.first_image.small }}" alt="{{ product_item.title }}" />
      </a>
    
      <h3 class="product-model">
        <a href="{{ product_item.url }}">
          {{ product_item.vendor }}
          {{ product_item.title }}
        </a>
      </h3>
    
      {% unit_price_for 'product_item' %}
    </div>
    

    Ejemplo de uso

    Usamos la etiqueta especial loop para llamar nuestra plantilla product_item.html con cada producto en la lista:

    <!-- products.html -->
    {% paginate products by 20 %}
      {% if paginated_list.size == 0 %}
        <p>No hay productos</p>
      {% else %}
        {% loop paginated_list in 'product_item' %}
        {{ paginated_list | pagination }}
      {% endif %}
    {% endpaginate %}
    

    De esta manera podemos reutilizar el mismo HTML para productos desde distintas plantillas. Estas plantillas reutilizables a veces son llamadas «parciales», porque muestran sólo una parte de la información de una página.

    Asimismo, ahora podemos incluir esta misma plantilla en el listado de productos similares en product.html:

    <!-- product.html -->
    {% if product.has_similar_products %}
      <div id="similar-products">
        <h2>Productos similares</h2>
        {% loop product.similar_products in 'product_item' limit:3 %}
      </div>
    {% endif %}
    

    {% paginate [list] by [num_per_page] %}

    {% paginate %} sirve para dividir listas de productos (o cualquier cosa iterable) en varias páginas. Dentro de cada bloque {% paginate %} existe la variable paginated_list con el grupo de objetos para la página actual.

    {% paginate products by 5 %}
      {% if paginated_list.size == 0 %}
        <p>No hay productos!</p>
      {% else %}
        {% for product in paginated_list %}
          <p>
            <a href="{{ product.url }}">{{ product.model }}</a>
            <span class="price">{{ product.price }}</span>
          </p>
        {% endfor %}
        {{ paginated_list | pagination }}
      {% endif %}
    {% endpaginate %}
    

    El ejemplo anterior crea grupos de 5 productos por página.

    También dentro del bloque paginate, el filtro pagination crea HTML para la navegación entre páginas.

    Paginacion de productos en plantillas Bootic

    {% unit_price %} y {% unit_price_for [product] %}

    Muestra el precio de un producto (en caso de que sea mayor a cero), junto con el precio de comparación, en caso de tener uno. También se puede usar en su variación unit_price_for [nombre_variable], en caso de que sea necesario pasar el nombre de la variable asociada al objeto producto.

    <!-- product.html -->
    <h3>{{ product.model }}</h3>
    
    {% unit_price %}
    

    {% stock_status %} y {% stock_status_for [product] %}

    Muestra el nivel de stock de un producto. Opcionalmente se le puede pasar el nombre de la variable asociada al producto, pero para eso hay que usar la etiqueta stock_status_for.

    <!-- product.html -->
    <h3>{{ product.model }}</h3>
    
    <div class="nivel-de-stock">
      {% stock_status %}
    </div>
    

    Además de esto, la etiqueta permite definir el número bajo el cual se considera que el stock está bajo, que por defecto es 5. Para eso le agregamos with_few_at [limite]. Por ejemplo, si hacemos lo siguiente:

    <!-- product.html -->
    <div class="nivel-de-stock">
      {% stock_status with_few_at 10 %}
    </div>
    

    Y el producto tiene 8 unidades en stock, el mensaje que se mostrará es "En stock" y no "Pocas unidades disponibles".

    Esta opción también se puede usar en conjunto con stock_status_at, obviamente:

    <!-- product.html -->
    <div class="nivel-de-stock">
      {% stock_status_for product_item with_few_at 10 %}
    </div>
    

    {% product_filters %}

    <!-- products.html -->
    {% unless ajax_request %}
    
      <!-- lo que está aquí adentro sólo se mostrará en la primera carga de la página
           y no cuando se carguen y muestren los resultados de búsqueda, dinámicamente (via AJAX) -->
    
      <div class="container">
    
        <h2 style="padding-top: 5%;">
          {% if view == 'search' %}
            Resultados de búsqueda
          {% elsif resource %}
            {{ resource.name }}
          {% else %}
            Productos
          {% endif %}
        </h2>
    
        <div id="filters-container">
          {% product_filters collections, tags, types in "#search-results" %}
        </div>
    
        <div id="search-results">
    
    {% endunless %}
    
        <!-- lo que está aquí se mostrará tanto en la carga inicial de la página
             como en los resultados cargados dinámicamente -->
    
        {% paginate products by 18 %}
    
          {% if paginated_list.size == 0 %}
    
            <p>No encontramos productos bajo este criterio.</p>
    
          {% else %}
            <ul class="product-list">
              {% loop paginated_list in 'product_item' %}
            </ul>
    
            {{ paginated_list | pagination }}
          {% endif %}
    
        {% endpaginate %}
    
    {% unless ajax_request %}
    
        <!-- y por último, cerramos los contenedores que abrimos arriba -->
    
      </div><!-- /search-results -->
    </div><!-- /container -->
    
    {% endunless %}
    
    

    Genera una lista de filtros para la búsqueda. Normalmente se usan en la página de productos y búsquedas. La sintaxis es:

    {% product_filters [listado de filtros] in [selector para resultados de búsqueda] %}
    

    Donde el primer parámetro (listado de filtros es requerido) y el segundo no.

    Por ejemplo, puedes hacer:

    {% product_filters price, tags %}
    

    O bien:

    {% product_filters collections, types in "#search-results" %}
    

    En este caso, se generará el HTML con el el elemento form para buscar productos incluyendo dos filtros, uno de colecciones y uno de tipos de producto. El buscador realiza la búsqueda en forma dinámica (via AJAX) e inserta los resultados el en contenedor que pases como parámetro.

    En caso de no pasar ese parámetro, o bien en caso de que no exista el contenedor, al cambiar el valor de uno de los filtros se gatillará una búsqueda normal (no AJAX).

    En el ejemplo que ves a la derecha, usamos product_filters dentro de la plantilla products.html, que además usamos para desplegar las búsquedas (es decir, en este theme no hay un search.html).

    Si deseas construir tus propios filtros y/o tener más control sobre las opciones, te recomendamos que mires la sección Buscador de productos más abajo.

    {% add_to_cart %} y {% add_to_cart [product_variable] %}

    Genera el HTML del botón 'Agregar al carro' junto a la lista de variantes, en caso de haber más de una.

    <!-- product.html -->
    <h3>{{ product.model }}</h3>
    {% add_to_cart %}
    

    La etiqueta también acepta el nombre de la variable del producto que quieres usar. Esto es necesario cuando el nombre de la variable no sea product, por ejemplo dentro de una plantilla como product_item.html:

    <!-- product_item.html -->
    <h3>{{ product_item.model }}</h3>
    {% add_to_cart_for product_item %}
    

    {% add_to_cart_no_variants %} y {% add_to_cart_no_variants_for [product_variable] %}

    Una versión del 'Agregar al carro' que no contiene listas de variantes. Útil en caso de que quieras poner el botón en listas de productos.

    <!-- products.html -->
    {% for product in products %}
      <h3>{{ product.model }}</h3>
      {% add_to_cart_no_variants %}
    {% endfor %}
    

    Al igual que add_to_cart, esta etiqueta también tiene una versión con variable add_to_cart_no_variants_for que recibe el nombre de la variable asociada al producto, ya sea product o product_item u otro.

    {% cart_table_template %}

    Incluye la plantilla Javascript usada en por el carro dinámico. No es necesario que la uses, a menos de que quieras usar el módulo JS CartModal.

    {% dynamic_cart_table %}

    Inyecta el carro dinámico, basándose en la plantilla Javascript entregada por cart_table_template. Requiere del objeto cart, por lo que sólo puede usarse en la plantilla cart.html.

    {% shipping_calculator %}

    Permite mostrar un ventana modal con el costo de envío a la zona geográfica que el comprador seleccione.

    Obviamente, solo mostrará un resultado si la zona geográfica seleccionada tiene costos de envío creada en tu tienda. En caso de que no hayan opciones de envío para la zona elegida, verá un mensaje diciendo "No hay opciones de envío para esa zona."

    Para usar esta funcionalidad ingresamos la etiqueta en la plantilla product.html:

    <!-- product.html -->
    (...)
    
    <h3>{{ product.model }}</h3>
    <div>{% unit_price %}</div>
    
    <div class="costos-de-envio">
      {% shipping_calculator %}
    </div>
    (...)
    

    Filtros Liquid

    Liquid define que el filtro va siempre después del texto o elemento en cuestión, separado por una barra |. Si el filtro espera parámetros (como en el caso del número de caracteres en truncate), éstos van luego de 2 puntos justo después del nombre del filtro. Es decir:

    {{ texto | filtro:parámetro1, parámetro2 }}
    

    No todos los filtros esperan parámetros. El filtro size, por ejemplo, retorna la cantidad de elementos en una lista de objetos.

    Este producto está en {{ product.collections | size }} colecciones.
    

    Si el producto pertenece a 3 colecciones, lo de arriba resulta en:

      Este producto está en 3 colecciones.
    

    Aquí enumeramos los filtros disponibles en las plantillas Bootic.

    Filtros de texto Bootic

    t(string)

    Cuando envolvemos entre llaves un objeto {{ title.home }}, Bootic nos retorna el valor "Portada", pero si este valor lo tenemos como un string no logrará resolverlo, con {{ 'titles.home' | t }} podemos obtener el valor aunque el objeto esté como una expresión string.

    to_slug(string)

    Convierte una cadena de texto en formato slug. liquid_html {% assign text = "foo bar very nice!!" %} {{ text | to_slug }} # return foo-bar-very-nice

    format_text(string)

    Convierte un texto en párrafos <p></p> según cada salto de línea {{ text | format_text }}.

    split_text(string)

    El uso práctico de esta etiqueta es poder conseguir una vista previa de los post de nuestro blog, ya que nos permite mostrar solo el título del post y nos facilita un anchor link <a> de "ver más", el cual nos llevará al post completo. {{ post_item | split_text }}.

    print_if_equal(string, one_string, two_string)

    Se imprimirá el primer parámetro solo si one_string es igual a two_string. liquid_html {{ "checked" | print_if_equal: "XX", "XX" }} # => "checked" {{ "checked" | print_if_equal: "X", "xx" }} # => ""

    print_if_matches(string, one_string, two_string)

    Se imprimirá el primer parámetro si one_string es una subcadena de two_string. liquid_html {{ "selected" | print_if_matches: "some", "something there" }} # => "selected" {{ "selected" | print_if_matches: "some", "anything there" }} # => ""

    print_if_included(string, two_string, array)

    Se imprimirá el primer parámetro si two_string se encuentra dentro de la lista del tercer parámetro. liquid_html {% assign list = ["Ho", "Hi", "He"] %} {{ "selected" | print_if_included: "Hi", list }} # => "selected" {{ "selected" | print_if_included: "Ha", list }} # => ""

    escape_html(string)

    Toma una cadena con caracteres html y reemplaza los caracteres con secuencias de escape (para que la cadena se pueda usar en un input, por ejemplo).

    Filtros de conjuntos

    Filtros de URLs

    Filtros de URLs de colecciones

    Ejemplo de uso:

    {{ collection | typed_collection_path:type }}
    

    Filtros de imágenes

    Archivos e imágenes

    ¿Plantillas o archivos?

    En el editor Bootic, puedes agregar archivos CSS o JS subiendo los archivos desde tu computador, o bien puedes hacerlo creando una nueva plantilla y pegando el contenido original. ¿Cuál es la diferencia?

    La primera y diferencia es que los archivos no se pueden modificar con el editor de código, ya que están pensados como archivos externos y no editables para usarlos en tu plantillas (imágenes, tipografías, etc).

    La segunda diferencia es que las plantillas tienen un límite máximo de tamaño de 64 KB, mientras que los archivos pueden pesar hasta 1MB. Esto por la misma razón de antes. Así que si quieres incluir una librería JS que pese más de 64 KB simplemente no podrás subirla como una plantilla.

    Cómo agregar una nueva plantilla JavaScript o CSS

    <html>
      <head>
        <title>{{ shop.name }}</title>
        {{ 'master.css' | asset_url | stylesheet_tag }}
      </head>
      <body>
        {{ content_for_layout }}
      </body>
    </html>
    

    El HTML generado sería:

    <html>
      <head>
        <title>Mi tienda</title>
        <link href="/themes/2345/master.css" rel="stylesheet" type="text/css">
      </head>
      <body>
        <h1>Hola, estas en la portada de mi sitio!</h1>
      </body>
    </html>
    

    En este caso mostraremos como ejemplo la creación de una plantilla CSS llamada master.css.

    Crear nueva plantilla

    Para insertar una hoja de estilos primero tenemos que crear una plantilla de tipo "css" y darle un nombre.

    Crear nueva plantilla

    Luego la vinculamos desde layout.html usando los filtros asset_url y stylesheet_tag. El primero nos da la ruta absoluta a la hoja de estilos en los servidores Bootic. La segunda crea una etiqueta HTML <style>.

    En el caso que quieras agregar código Javascript de tu tienda, una vez creada la plantilla, tendrías que reemplazar master.css por el nombre de la plantilla JS, y stylesheet_tag por javascript_tag:

    (...)
      <head>
        <title>{{ shop.name }}</title>
        {{ 'theme.js' | asset_url | javascript_tag }}
      </head>
    (...)
    

    ¡Y boom! Listaylor.

    Librerías JavaScript y CSS externas

    Puede que quieras usar librerías externas en tu plantilla. Un ejemplo común es incluír y usar jQuery como complemento a tu propio código Javascript.

    Incluyendo una librería como archivo

    Incluyendo jQuery como archivo

    Si quieres usar una librería externa como jQuery, Velocity.js, Move.js, Vue.js u otras donde no vas a meter mano puede ser recomendable subirla como archivo en la sección "subir archivo" del editor.

    A continuación lo incluyes en layout.html de la misma manera.

    Incluyendo jQuery como archivo

    Incluyendo una librería desde una URL externa.

    Muchas librerías estan alojadas en servidores públicos especializados. Google, por ejemplo, provee versiones actualizadas de las librerías más comunes en esta página: http://code.google.com/apis/libraries/devguide.html#jquery

    En el caso particular de jQuery y cualquier librería alojada en los servidores de Google, recomendamos usar este método. Simplemente copia la dirección del archivo elegido en layout.html.

    Incluyendo jQuery como archivo

    Nótese que estamos usando el filtro javascript_tag del sistema de plantillas de Bootic. Lo anterior es equivalente a escribir el siguiente HTML:

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.5.2/jquery.min.js"
    type="text/javascript"></script>
    

    Trabajando con imágenes

    Subiendo imágenes en el editor

    Subir imágenes

    Abajo a la derecha en el editor de plantillas puedes subir archivos de tipo .gif, .png y .jpg. Bootic te muestra los archivos subidos con su nombre completo.

    Estas imágenes puedes ser usadas tanto en tus plantillas CSS como HTML.

    Insertando imágenes en plantillas HTML

    Abre la plantilla que quieras (por ejemplo layout.html) y agrega una etiqueta de image &lt;img&gt;. En el atributo src debes insertar el nombre del archivo que subiste, tal cual aparece en la lista de archivos subidos, usando el filtro asset_url, así:

    <img src="{{ 'logo.png' | asset_url }}" alt="Logo de mi tienda" />
    

    El filtro asset_url genera la URL al directorio donde Bootic guarda tus archivos. Una vez que guardas la plantilla, Bootic genera dinámicamentte la ruta completa, que se ve más o menos así:

    <img src="http://static.bootic.net/uploads/themes/32/logo.png" alt="Logo de mi tienda" />
    

    Insertando imágenes en plantillas CSS

    En CSS basta con usar el nombre de la imágen directamente. Bootic guarda las plantillas CSS y las imágenes subidas en el mismo directorio, por lo que no es necesario usar el filtro asset_url.

    Este ejemplo situa la imagen "fondo.gif" como imagen de fondo del elemento body (por ejemplo en la plantilla master.css)

    body {
      font-size: 12px;
      background: url(fondo.gif);
    }
    

    Favicon

    El "favicon" es el ícono del sitio que aparece en la barra de título del navegador, y es simplemente una imagen que puedes insertar en la plantilla layout.html. Los favicons normalmente están optimizados y tienen la extensión ".ico", y los puedes generar a partir de una imagen en otro formato (PNG, GIF, JPG) usando un editor de imágenes como Photoshop o incluso herramientas gratuitas online (puedes encontrar muchas en Google: http://www.google.com/search?q=favicon+generator)

    Sube una imágen de 16x16px a la lista de archivos de tu editor de plantillas Bootic e inserta el siguiente código HTML en la sección HEAD de la plantilla layout.html.

    <link rel="shortcut icon" href="{{ 'favicon.ico' | asset_url }}" type="image/x-icon" />
    

    "favicon.ico" es la convención para estos archivos pero en verdad puedes nombrarlo como quieras.

    Enumerando imágenes de productos

    Si quieres listar sólo un subconjunto de las imágenes de un producto, puedes usar el filtro limit:

    {% for image in product.images limit:2 %}
      <div class="product-image">
        {% image.large | image_tag %}
      </div>
    {% endfor %}
    

    En este caso sólo se mostrarán las primeras dos imágenes del producto.

    El fragmento a continuación muestra el resto de las imágenes de un producto (si es que tiene) en un tamaño menor ("thumbnail", de 75x75px).

    {% if product.images.size > 2 %}
      {% for image in product.images offset:2 %}
      <div class="product-image">
        <a href="{{ image.large }}">
          {% image.thumbnail | image_tag %}
        </a>
      </div>
      {% endfor %}
    {% endif %}
    

    Cada imagen de producto tiene los siguientes tamaños predefinidos:

    Además de los tamaños predefinidos, puedes asignar dimensiones personalizadas a imágenes de productos usando el filtro resize, descrito a continuación.

    Control avanzado de imágenes

    Además de los tamaños predefinidos, puedes asignar dimensiones personalizadas a imágenes de productos usando el filtro resize.

    <img src="{{ image | resize: '100x100#c' }}" alt="{{ product.model }}" />
    

    El ejemplo anterior crea un cuadrado de 100x100px, centrado, a partir de la imagen de un producto.

    La lista de geometrías de imagen reconocidas por resize es:

    Imágenes de productos en tono de grises (blanco y negro)

    Puedes usar el filtro greyscale para transformar la imagen a blanco y negro.

    <img src="{{ image | resize: '100x100#c' | greyscale }}" alt="{{ product.model }}" />
    

    El orden no importa:

    <img src="{{ image | greyscale | resize: '100x100#c' }}" alt="{{ product.model }}" />
    

    Si usas greyscale sin el filtro resize, la imagen retornada tiene dimensiones por defecto de '240x180'.

    <img src="{{ image | greyscale }}" alt="{{ product.model }}" />
    

    Buscador de productos

    En Bootic usamos un motor fulltext para las tiendas y esto nos permite hacer todo tipo de cruces y filtros a la hora de realizar una búsqueda. Si bien puedes puedes hacer una búsqueda simple usando la URL /search?q=busqueda, tambien puedes incluir parámetros para filtrar la lista de resultados.

    Los parámetros disponibles a usar en el buscador son los siguientes. Todos ellos son opcionales, pero obviamente se espera que pases al menos uno.

    Ejemplos de filtros de búsqueda

    La manera más fácil de agregar filtros de búsqueda a tu tienda es usando la etiqueta {% product_filters %}, descrita aquí arriba. Sin embargo, hay casos en que querrías armar tus propios filtros. Si es así, ¡esta sección es para tí!

    Vamos a partir generando una lista de filtros básica (precio y orden) que podrías incluir en products.html a través de un partial. En caso de que tu plantilla tenga un search.html, también debes incluirla ahí ya que ésa es la plantilla usada para desplegar los resultados de búsqueda en caso de que exista (de lo contrario se usa products.html también).

    <form action="/search" method="get" class="product-filters">
      {% if collection %}
        <!-- nos aseguramos de incluir la coleccion actual en la nueva búsqueda -->
        <input type="hidden" name="collections" value="{{ collection.slug }}" />
      {% endif %}
    
      {% if product_type %}
        <!-- nos aseguramos de incluir el tipo de producto actual en la nueva búsqueda -->
        <input type="hidden" name="product_types" value="{{ product_type.slug }}" />
      {% endif %}
    
      <!-- filtro por precio -->
      <label for="price-range-select" class="search-filter-label">Filtrar por precio</label>
      <select id="price-range-select" class="search-filter" name="price_range" style="width: 190px; margin: 0 20px 0 7px">
        <option value="">Todos</option>
        <option {% if params.price_range == "-15000"> %}selected{% endif %} value="-15000">De 0 a $15.000</option>
        <option {% if params.price_range == "15000-30000" %}selected{% endif %} value="15000-30000">De $15.000 a $30.000</option>
        <option {% if params.price_range == "30000-50000" %}selected{% endif %} value="30000-50000">De $30.000 a $50.000</option>
        <option {% if params.price_range == "50000-100000" %}selected{% endif %} value="50000-100000">De $50.000 a $100.000</option>
        <option {% if params.price_range == "100000-"> %}selected{% endif %} value="100000-">Más de $100.000</option>
      </select>
    
      <!-- ordenar por -->
      <label for="sort-select" class="search-filter-label">Ordenar por</label>
      <select id="sort-select" class="search-filter" name="sort" style="width: 190px; margin: 0 20px 0 7px">
        <option value="">Relevancia</option>
        <option {% if params.sort == "title.asc" %}selected{% endif %} value="title.asc">Nombre</option>
        <option {% if params.sort == "price.asc" %}selected{% endif %} value="price.asc">Precio de menor a mayor</option>
        <option {% if params.sort == "price.desc" %}selected{% endif %} value="price.desc">Precio de mayor a menor</option>
        <option {% if params.sort == "discount_percentage.asc" %}selected{% endif %} value="discount_percentage.asc">De menor a mayor descuento</option>
        <option {% if params.sort == "discount_percentage.desc" %}selected{% endif %} value="discount_percentage.desc">De mayor a menor descuento</option>
      </select>
    </form>
    

    A continuación mostramos una versión más avanzada de filtros de búsqueda, que incluye selector de tipo, marca y etiqueta que se ajustan según la colección activa (sólo se muestran los tipos o marcas de productos que son parte de la colección y no todas). Además permite filtrar por varias entidades del mismo tipo simultáneamente (ej: quiero ver al mismo tiempo productos del tipo Zapatillas y los del tipo Botas).

    <form action="/search" method="get" class="filtros-producto">
      <!-- este parámetro le dice al endpoint /search que despliegue los resultados usando products.html y no search.html -->
      <input type="hidden" name="_template" value="products" />
    
      {% if collection %}
        <input type="hidden" name="collections" value="{{ collection.slug }}" />
      {% endif %}
    
      <!-- filtro por tipo de producto -->
      <div class="panel">
        <ul>
        {% if collection %}
          {% assign type_list = collection.product_types %}
        {% else %}
          {% assign type_list = shop.product_types %}
        {% endif %}
    
        {% for t in type_list %}
          <li>
            <input id="type-{{ t.slug }}-cb" {% if resource == t %}checked{% endif %} {{ 'checked' | print_if_matches: params.product_types, t.slug }} type="checkbox" name="product_types[]"  value="{{ t.slug }}"  />
            <label for="type-{{ t.slug }}-cb">{{ t.name }}</label>
          </li>
        {% endfor %}
        </ul>
      </div>
    
      <!-- filtro por marca -->
      <div class="panel">
        <ul>
        {% if collection %}
          {% assign vendor_list = collection.vendors %}
        {% else %}
          {% assign vendor_list = shop.vendors %}
        {% endif %}
    
        {% for v in vendor_list %}
          <li>
            <input id="vendor-{{ v.slug }}-cb" {% if resource == v %}checked{% endif %} {{ 'checked' | print_if_matches: params.vendors, v.slug }} type="checkbox" name="vendors[]" value="{{ v.slug }}" />
            <label for="vendor-{{ v.slug }}-cb">{{ v.name }}</label>
          </li>
        {% endfor %}
        </ul>
      </div>
    
      <!-- filtro por etiqueta -->
      <div class="panel">
        <ul>
        {% if collection %}
          {% assign tag_list = collection.tags %}
        {% else %}
          {% assign tag_list = shop.tags %}
        {% endif %}
    
        {% for t in tag_list %}
          <li>
            <input id="tag-{{ t.slug }}-cb" {% if resource == t %}checked{% endif %} {{ 'checked' | print_if_matches: params.tags, t.slug }} type="checkbox" name="tags[]" value="{{ t.slug }}" />
            <label for="tag-{{ t.slug }}-cb">{{ t.name }}</label>
          </li>
        {% endfor %}
        </ul>
      </div>
    </form>
    
    <script>
      $(function() {
        $('.filtros-producto input[type=checkbox]').on('change', function() {
          this.form.submit();
        })
      })
    </script>
    
    

    Slider de precio mínimo y máximo

    En caso de que quieras mostrar un selector de precio tipo "slider", con rangos deslizables, puedes usar el parámetro price_gte y price_lte que contienen los valores desde/hasta de price_range. De hecho, puedes enviar price_gte y price_lte como parámetros en vez de price_range, ya que price_range parsea y convierte los valores internamente en price_gte y price_lte.

    Es decir, si usas una librería como range.slider, puedes hacer lo siguiente:

    <input class="slider-precio" name="price_range" />
    
    <script>
    {% assign max_price = 50000 %}
    
    $(".slider-precio").ionRangeSlider({
      type: "double",
      grid: true,
      min: 0,
      max: {{ max_price }},
      from: {{ params.price_gte | default: 0 }},
      to: {{ params.price_lte | default: max_price }},
      prefix: "$"
    });
    </script>
    

    Filtrando con atributos y variantes de producto

    Ahora, si quieres enumerar los atributos de producto para que actúen como filtros, lo puedes hacer así:

    {% if collection %}
      {% assign product_attributes = collection.product_attributes %}
    {% else %}
      {% assign product_attributes = shop.product_attributes %}
    {% endif %}
    
    <h3>Atributos</h3>
    {% for attr in product_attributes %}
      <span class="attribute-filter {{ attr.slug }}-filter">
      <select name="attributes[{{ attr.slug }}]">
        <option value="">{{ attr.name }}</option>
        {% for val in attr.values %}
          {% assign key_val = attr.slug | append: ":" | append: val %}
          <option {{ 'selected' | print_if_matches: params.attributes, key_val }} value="{{ val }}">{{ val }}</option>
        {% endfor %}
      </select>
      </span>
    {% endfor %}
    

    En caso de que quieras esconder uno u otro atributo específico, lo puedes hacer con CSS.

    Bootic.js y los módulos Javascript

    Descripción

    Se dividen en dos grupos:

    Módulos Base

    Módulos Extra (dependen de los de arriba)

    ¿Qué son y para qué sirven?

    // product.liquid
    
    {% for image in product.images %}
    <div>
      <a class="slide" href="{{ image.large }}" data-related-variant-id="{{ image.related_variant_id }}">
        <img src="{{ image.thumbnail }}" alt="{{ product.model }}" />
      </a>
    </div>
    {% endfor %}
    
    <script>
     Bootic.load('VariantImages', {
       imageSelector: '.slide'
     })
    </script>
    

    En general, los módulos JS sirven para modificar el comportamiento de la interfaz de usuario (UI).

    Por ejemplo, el módulo VariantImages se usa en las fichas de producto para cambiar la imagen desplegada según la variante elegida por el usuario. El ejemplo que ves a la derecha muestra cómo se usa.

    En este caso, la plantilla genera una lista de divs que contienen las imágenes de un producto. Al cargar VariantImages, le indicamos cuál es el selector que corresponde a las imágenes. Internamente, el módulo "escucha" cuando un usuario cambia la variante seleccionada, y cuando esto ocurre, busca dentro de los elementos con el selector indicado y gatilla un evento click sobre él (para usarlo con librerías de slideshows o galerías de imágenes).

    Este módulo viene incluido por defecto en la plantilla Oxygen y puedes ver este comportamiento en alguno de los productos de la tienda de demostración.

    Pero eso lo puedo hacer por mi cuenta, ¿no?

    Sí, claro que lo puedes hacer a mano por tu cuenta. Los módulos JS los hacemos simplemente para ahorrarte tiempo en cosas chicas para que puedas enfocarte en otras cosas más importantes.

    La gracia, también, es que los módulos los vamos actualizando en el tiempo, entonces en caso de que hayan cambios importantes (por ejemplo en la demarcación del botón "Agregar al carro") no será necesario que actualices tu código para que el truco que hiciste siga funcionando.

    (Tampoco es que estemos cambiando el HTML del "Agregar al carro" todos los días, pero igual. :)

    Cómo usar los módulos

    Paso 1: Cargar dependencias

    <!-- layout.html -->
    
    <!doctype html>
    <html lang="es">
    
    <head>
      <meta charset="utf-8" />
      <meta name="description" content="{% current_description %}" />
      <meta name="viewport" content="width=device-width, initial-scale=1.0" />
      <title>{% current_title %} - {{ shop.name }}</title>
    
      {% bootic_header 'v2' %}
      {{ 'styles.css' | asset_url | stylesheet_tag }}
    </head>
    
    <body>
      <header>
        <ul class="menu">
          {% for link in menus.main.links %}
          <li><a href="{{ link.url }}">{{ link.title }}</a></li>
          {% endfor %}
        </ul>
      </header>
    
    {{ content_for_layout }}
    
    <footer>
      {{ pages.footer.body }}
    </footer>
    
    {{ 'theme.js' | asset_url | async_javascript_tag }}
    {% bootic_footer 'v2' %}
    
    </body>
    </html>
    

    Lo primero que necesitas es cargar el código Javascript que incluye los módulos para poder usarlos. Este código viene incluido en el archivo shops.js que es cargado, a su vez, por la etiqueta {% bootic_footer 'v2' %}.

    Esta etiqueta se usa normalmente al final de la plantilla layout.html, y la parte clave es el v2, que le indica a Bootic que deseas cargar la segunda versión del pie de página. Si omites esa parte, Bootic no incluirá la etiqueta <script> hacia shops.js.

    Como complemento a esto, la etiqueta {% bootic_header 'v2' %} agrega en el encabezado un trozo de código que simplifica la carga de módulos y además te permite hacerlo aún cuando shops.js no esté 100% cargado.

    El ejemplo de la derecha muestra una versión simplificada de layout.html con ambas etiquetas en forma correcta.

    Paso 2: Bootic.require() y Bootic.load()

    Una vez que tienes la versión 2 (v2) de bootic_header y bootic_footer en layout.html, ya puedes ponerte a jugar. La manera normal de usar un módulo es, normalmente, requerirlo usando Bootic.require y luego llamar alguno de sus métodos con los parámetros que corresponda.

    Por lo general, los módulos adicionales sólo exponen dos métodos, load() y unload(), y por lo mismo ofrecemos una manera de llamarlos usando Bootic.load() y Bootic.unload() para ahorrarte líneas de código.

    <!-- alguna plantilla, por ejemplo layout.html -->
    
    <script>
      // para usar un módulo base, lo cargamos con Bootic.require()
      Bootic.require('Cart', function(cart) {
        alert('Hay ' + cart.units + ' productos en tu carro!')
      })
    
      // y lo mismo aplica para los módulos adicionales
      Bootic.require('CartModal', function(cartModal) {
        cartModal.load({ cartLinkSelector: '.show-cart' })
      })
    
      // sin embargo, Bootic.load() nos permite saltarnos el paso de require()
      Bootic.load('CartModal', { cartLinkSelector: '.show-cart' })
    </script>
    

    Los módulos base, en cambio, están pensados como herramientas para ser usados de distintas formas, por lo que no responden a los métodos load() y unload(), a diferencia de los módulos adicionales. Más abajo puedes ver los métodos que cada módulo expone para que uses.

    Paso 3: Listo!

    Eso es todo, así es como se usan los módulos Javascript Bootic. A continuación detallamos los distintos módulos y lo que hace cada uno.

    Cart

    <script>
      // requerimos el módulo Cart usando Bootic.require()
      Bootic.require('Cart', function(cart) {
        // Cart es un objeto observable, permitiendo definir
        // funciones que se invocan cuando ocurran ciertos eventos
        cart.on('updated', function() {
          // en este caso, vamos a mantener actualizado un contador con las unidades
          // en el carro cada vez que se produzca alguna actualización.
          $('#cart-count').text(cart.units);
        })
      })
    </script>
    

    Cart es uno de los módulos base y permite realizar modificaciones en el carro del usuario, o simplemente reaccionar a ciertos eventos cuando estos ocurran.

    Métodos

    .isLoading() # => Boolean

    Retorna un booleano indicando si el carro está en proceso de carga o no.

    .isEmpty() # => Boolean

    Retorna un booleano indicando si el carro está vacío o no.

    .add(variantId[, quantity, callback]) # => Cart

    Agrega quantity unidades de la variante con ID variantId al carro (por defecto una unidad). Opcionalmente se puede pasar una función como último argumento, que es llamada luego de que el carro se sincroniza con el backend.

    .find(variantId[, callback]) # => Object/Null

    Busca y retorna el objeto asociado a la variante con el ID variantId que haya dentro del carro. En caso de no haber, retorna null. Opcionalmente se puede pasar una función que es llamada con el mismo resultado retornado.

    .findByProductId(productId[, callback]) # => Object

    Busca y retorna el objeto asociado a la variante perteneciente al producto con ID productId que haya dentro del carro. En caso de no haber, retorna null. Opcionalmente se puede pasar una función que es llamada con el mismo resultado retornado.

    .forEach(callback[, context]) # => Cart

    Itera sobre los productos en el carro, invocando la función callback (requerida), opcionalmente dentro del contexto context, que por defecto corresponde a la instancia propia de Cart.

    .remove(variantId[, callback]) # => Cart

    Remueve la variante con ID variantId del carro. Opcionalmente se puede pasar una función como último argumento, que es llamada luego de que el carro se sincroniza con el backend.

    Eventos

    El objeto Cart es un Observable, así que puedes también escuchar a ciertos eventos y reaccionar a ellos.

    Evento 'updated'

    <script>
    Bootic.require('Cart', function(cart) {
      cart.on('updated', function() {
        console.log('En este momento hay ' + cart.units + ' productos en el carro.');
      })
    
      cart.on('loading', function() {
        document.getElementById('spinner').style.display = 'inline-block';
      })
    
      cart.on('loaded', function() {
        document.getElementById('spinner').style.display = 'none';
      })
    
      cart.on('adding', function(obj) {
        alert('Agregando ' + obj.quantity + ' unidades de la variante ' + obj.variantId)
      })
    
      cart.on('added', function(item) {
        console.log('Item agregado!', item);
      })
    
      cart.on('removing', function(item) {
        console.log('Removiendo item del carro...', item);
      })
    
      cart.on('removed', function(item) {
        console.log('Item removido!', item);
      })
    })
    </script>
    

    Llamado cada vez que se actualiza el carro, ya sea al inicializarse o cuando sus contenidos se actualizan por medio de los métodos .add() o remove().

    Evento 'loading'

    Llamado antes de hacer el llamado al backend para actualizar los datos del carro.

    Evento 'loaded'

    Llamado luego de hacer el llamado al backend para actualizar los datos del carro.

    Evento 'adding'

    Llamado antes de hacer el llamado al backend para agregar un producto al carro. Recibe un objeto con los parámetros variantId y quantity.

    Evento 'added'

    Llamado luego de hacer el llamado al backend para agregar un producto al carro. Recibe un objeto item con los datos del item recién agregado.

    Evento 'removing'

    Llamado antes de hacer el llamado al backend para remover un producto al carro. Recibe un objeto item con los datos del item siendo removido.

    Evento 'removed'

    Llamado luego de hacer el llamado al backend para remover un producto al carro. Recibe un objeto item con los datos del item removido.

    <script>
    Bootic.require('Modal', function(modal) {
      var body  = '<h1>Hola mundo!</h1>';
          body += '<p>Este es un hermoso modal.</p>';
    
      modal.html(body);
    })
    </script>
    

    Módulo que permite mostrar modales en forma rápida y sin ningún tipo de dependencias adicionales.

    Métodos

    <script type="text/html" id="my-modal-template">
      <h1><% title %></h1>
      <p><% message %></p>
      <button class="close-button">Cerrar</button>
    </script>
    
    <script>
    // en este ejemplo requerimos también el módulo Templates, para construir
    // el cuerpo del modal a partir de la plantilla definida aquí arriba.
    Bootic.require('Modal', 'Templates', function(modal, templates) {
      // construimos el HTML usando el módulo Templates
      var templateHTML = document.getElementById('my-modal-template').innerHTML,
          body = templates.renderRaw(templateHTML, { title: 'Hola!', message: 'Un mensaje' });
    
      // mostramos el modal, y guardamos la instancia retornada en la variable `box`
      var box = modal.html(body);
    
      // y agregamos un listener para cerrar el modal si el usuario apreta el botón
      $(box).on('click', '.close-button', function() {
        modal.hide(box);
      })
    })
    </script>
    

    .html(body, opts) # => modal

    Construye y despliega (vía .show()) un nuevo modal a partir del HTML recibido en el primer argumento (body). opts es un objeto que puede contener opciones para la construcción del nodo HTML, como por ejemplo className.

    .iframe(src, opts) # => modal

    Construye un y despliega un modal cuyo contenido estará definido por la ruta recibida en src. El objeto opts puede contener una propiedad height así como las propiedades usadas en la construcción del HTML descritas en el método anterior, .html().

    .show(modal, opts) # => modal

    Despliega un modal ya construido por .html() o .iframe(). Útil para cuando desees reutilizar un mismo modal, sin tener que reconstruirlo cada vez.

    .hide(modal) # => modal

    Esconde un modal ya desplegado. El argumento puede ser tanto un modal como un string con el selector HTML (ID o clase) que corresponda al modal en cuestión.

    Eventos

    Evento: 'hidden'

    Llamado cuando un modal es escondido, ya sea vía .hide() o simplemente porque el usuario hizo click sobre la cruz para cerrarlo.

    Notice

    <script>
    Bootic.require('Notice', function(notice) {
      // esperamos tres segundos y mostramos el aviso
      var message = '<p>Envío <u>gratis a todo Chile</u> por compras sobre <strong>$50.000</strong></p>!';
      setTimeout(function() {
        notice.fromContent('envio-gratis', message);
      }, 3000);
    })
    </script>
    

    Permite mostrar avisos en una franja que se despliega en la parte superior de la ventana del navegador. Utilizado por el módulo PromoNotice para notificar cuando hay una promoción activa en el carro.

    Métodos

    .fromContent(noticeName, content)

    <script type="text/html" id="my-notice-template">
      <% #isEndOfMonth? %>
        <p>Está terminando el mes! <% discountPercentage %> de descuento en todas las compras sobre <% discountMinPurchase %>.</p>
      <% _else %>
        <p>Bienvenido! Si necesitas ayuda en tu compra llámanos al <% phoneNumber %>.</p>
      <% /isEndOfMonth? %>
    </script>
    
    <script>
    Bootic.require('Notice', function(notice) {
      notice.fromTemplate('aviso-del-dia', 'my-notice-template', {
        isEndOfMonth: new Date().getDay() > 25,
        discountMinPurchase: '$35.000',
        discountPercentage: '10%',
        phoneNumber: '+56 (9) 2345 6789'
      })
    
      notice.whenShown(function(val) {
        console.log(val)
      })
    })
    </script>
    

    Genera y muestra un aviso con el contenido content. noticeName debería ser un nombre único que identifique el tipo de aviso, ya que se usa para identificar cuando un aviso debiese "tapar" a otro existente y cuando no.

    .fromTemplate(noticeName, templateId, data)

    Genera y muestra un aviso generado a partir de la plantilla HTML con el selector templateId y los datos que vengan dentro del objeto data. noticeName debería ser un nombre único que identifique el tipo de aviso, ya que se usa para identificar cuando un aviso debiese "tapar" a otro existente y cuando no.

    .remove(noticeName)

    Esconde el aviso con el nombre noticeName, en caso de haber uno.

    .isShown(noticeName)

    Retorna true si existe algún aviso y opcionalmente chequea si el nombre del aviso es igual a noticeName

    .whenShown(cb)

    Al aparecer un aviso se ejecuta el listener para retornar un callback con el noticeName.

    .show(msg, isWarning)

    Muestra el aviso que escribamos en msg. Si definimos isWarning como tue el aviso se pintará amarillo.

    Templates

    Módulo para construir fragmentos de HTML en base a plantillas (templates) y objetos Javascript. Usamos la librería qiq escrita por uno de los nuestros, que está basada en Mustache pero cuenta con algunos trucos adicionales.

    Para escribir tus propias plantillas te sugerimos que las incluyas dentro de una etiqueta <script>. Un dato importante es que qiq por defecto usa las etiquetas de tipo bigote ({{ }}), sin embargo nosotros usamos la opción de qiq que permite cambiarlas por otras (<% %>). Esto lo hacemos para evitar confusiones con las etiquetas que usa nuestro lenguaje de plantillas, Liquid, que son también los bigotes {{ }} y bigotes con porcentajes {% %}.

    Métodos

    compile(templateName, body)

    Compila una plantilla con cuerpo body llamada templateName. Este último debiese ser un nombre único, ya que este módulo lo usa internamente para construir un caché y evitar compilar una misma plantilla dos veces.

    isCompiled(templateName) # => Boolean

    Retorna un booleano indicando si existe o no una plantilla compilada en el caché con el nombre templateName.

    renderCached(templateName, data) # => String (HTML)

    Renderea una plantilla ya compilada con los datos contenidos en el objeto data. Retorna el HTML generado.

    renderRaw(body, data) # => String (HTML)

    Compila y renderea la plantilla contenida en body con los datos contenidos en el objeto data. Retorna el HTML generado.

    Util

      <script>
        // requerimos el módulo Util usando Bootic.require()
        Bootic.require('Util', function(util) {
          // Util es un objeto con multiples métodos
          // en este caso obtenemos el primer elemento que contiene la query
          // e imprimos por consola el texto que contiene. 
          var priceContainer = util.get('.bootic-price')
          console.log(util.text(priceContainer))
        })
      </script>
    

    Pequeña librería utilitaria —desarrollada por Bootic—que proporciona al desarrollador un variado grupo de funciones. Algo así como un jQuery pero mucho acotado, con lo justo y necesario para que el resto de los módulos funcionen sin la necesidad de librerías externas.

    Métodos Nodo

    Con estos métodos vas a poder manejar de forma sencilla las propiedades de los nodos, estos metodos requieren como primer parametro el nodo que se quiere modificar.

    .prepend/append/remove(el)

      <script>
        // inserta el nodo "el" como el primer hijo.
        util.prepend(parentNode, el);
        // inserta el nodo "el" como el ultimo hijo
        util.append(parentNode, el);
        // elimina el nodo "el" del dom
        util.remove(el)
      </script>    
    

    prepend y append insertan un nodo al inicio o al final de un nodo padre y remove elimina un nodo del dom.

    <script>
    
    // ejemplo de uso
    
    Bootic.require('Util',function(util){
    
      var logo = util.get('#logo');
    
      setTimeout(function(){
        util.hide(logo)
      },3000)
    
      setTimeout(function(){
        util.show(logo)
      },6000)
    
      setTimeout(function(){
        util.css(logo,'border','2px dotted red')
      },9000)
    
      setTimeout(function(){
        util.html(logo,'Esto es una locura')
      },12000)
    
    
    });
    
    </script>
    
    
    

    .css(el, prop, value)

    Modifica el valor de una propiedad css de un elemento. por ejemplo: util.css(node, 'display', 'flex')

    .show/hide/toggle(el)

    Controlan los valores de la propiedad display de un elemento para mostrarlo u ocultarlo en la UI. El método show acepta un segundo parametro con el valor de la propiedad display que desas establecer flex, block, etc.

    .hasClass/addClass/removeClass/toggleClass(el, className)

    Métodos que manejan las clases de un nodo, hasclass retorna true o false dependiendo si el elemento tiene o no la clase a buscar. addClass, removeClass y toggleClass agregan y remueven la clase ingresada.

    .val/text/html(el, str)

    Son equivalentes a los métodos value, innerText y innerHtml del DOM. Para insertar un valor se necesita ingresar dos parametros, el elemento y un string con el valor a cambiar util.text(el, 'Hello world''). Tambien puedes obtener el valor del elemento, ingresando solo el primer argumento util.val(inputNode)

    .children/parent/next/prev/siblings(el)

    Gracias a estos métodos puedes navegar por el DOM desde un elemento/nodo de referencia, puedes usar un selector como segundo parametro en los métodos parent y sibligns para buscar un nodo en especifico, por ejemplo: util.siblings(el, '.form-control'), util.parent(el, 'form').

    Métodos DOM

    .on/off/once(el, type, selector, callback)

    Metodos para manejar eventListeners, permiten agregar y eliminar eventListeners de un elemento. El argumento selector es opcional, y sirve para buscar al padre del elemento mediante un selector para que escuche el evento que se le desa agregar. liquid_html <script> util.on(el, 'click', function (evt) { console.log(evt.target) }) </script>

    .ready(callback)

    Ejecuta un callback cuando el documento HTML ha sido completamente cargado.

    .extend(targetObj, sourceObj)

    Extiende un objeto con los atributos de otro. Si el objeto target y source tienen el mismo atributo, el atributo de target se sobrescribe por el de source.

    .find(selector, context)

    Retorna un array de nodos que cumplen con el selector, el parametro context es opcional y es el nodo donde se realiza la busqueda, de no ingresarlo toma como valor el objeto document.

    .get(selector, context)

    Retorna el primer elemento que cumple con el selector.

    .ajax(method, url, data, onSuccess, onError)

    Hace peticiones ajax usando callbacks.

    .sendForm(form, action, onSuccess, onError)

    Envía un formulario mediante ajax, onSuccess y onError son callbacks para manejar las respuestas.

    .getCookie/delCookie/setCookie(name, value, expiration)

    Manejan las cookies, getCookie y delCookie solo aceptan el parametro name, setCookie tiene como parametros el nombre, valor y tiempo de vida de la cookie en días.

    .store()

    Para manejear localStorage,en caso de el navegador no soporte localStorage este método usa por debajo cookies.

    CartModal

    // layout.html
    <header>
      <a href="/cart" class="cart-link">Ver carro</a>
    </header>
    
    {% content_for_layout %}
    
    <!-- la siguiente etiqueta incluye la plantilla HTML del carro
         por defecto de Bootic, usando el ID 'cart-table-template' -->
    {% cart_table_template %}
    
    <script>
      Bootic.load('CartModal', { cartLinkSelector: '.cart-link' });
    </script>
    

    Una buena recomendación es no cargar CartModal en caso de que ya haya una instancia del carro dinámico, que ocurre cuando el usuario está en la ruta /cart. Para eso puedes usar Bootic.loaded(), de esta forma:

    <script>
      if (!Bootic.loaded('DynamicCartTable')) {
        Bootic.load('CartModal', { cartLinkSelector: '.cart-link' });
      }
    </script>
    

    Si quieres usar una plantilla propia para generar el carro, sería algo así:

    // layout.html
    <header>
      <a href="/cart" class="cart-link">Ver carro</a>
    </header>
    
    {% content_for_layout %}
    
    <script type="text/html" id="my-super-cart-template">
      <% #products? %>
        <ul>
        <% #products %>
          <li>
            <a href="/products/<% slug %>"><% model %></a>
            <% custom_variant_title? %>
              <em><% variant_title %></em>
            <% /custom_variant_title? %>
          </li>
        <% /products %>
        </ul>
      <% /products? %>
    </script>
    
    <script type="text/javascript">
      Bootic.load('CartModal', {
        templateId: 'my-super-cart-template',
        cartLinkSelector: '.cart-link'
      });
    </script>
    

    Adicionalmente, puedes también mostrar productos relacionados en el modal para que estos se muestren cuando se agreguen productos al carro (ver módulo DynamicCartButtons, abajo). Para eso debes pasar la opción productsTemplateId:

    <script type="text/html" id="related-products-template">
      <% products? %>
      <div class="product-list">
        <h2>También te puede interesar</h2>
        <ul>
        <% #products %>
          <li>
            <% image? %>
              <a href="/products/<% slug %>">
                <img src="<% image %>" alt="<% model %>" />
              </a>
            <% /image? %>
            <h3><a href="/products/<% slug %>"><% model %></a></h3>
            <strong>$<% price %></strong>
          </li>
        <% /products %>
        </ul>
      </div>
      <% /products? %>
    </script>
    
    <script type="text/javascript">
      if (!Bootic.loaded('DynamicCartTable')) {
        Bootic.load('CartModal', {
          // show cart modal when clicking '.cart-link'
          cartLinkSelector: '.cart-link',
          // and use this template for rendering related products
          productsTemplateId: 'related-products-template'
        });
      }
    </script>
    

    Permite mostrar un modal y desplegar el carro de compras en él, en forma automática cuando el usuario haga click en un elemento de la página, o bien cada vez que se gatille el evento 'updated' del carro.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    cartLinkSelector El selector del elemento que abrirá el modal al ser clickeado Ninguno .cart-link
    templateId El ID de la plantilla HTML a usar para generar el carro No cart-table-template
    productsTemplateId El ID de la plantilla HTML a usar para mostrar productos relacionados No Ninguno related-products-template

    CartUnits

    Dinámicamente enumera la cantidad de productos en el selector cuando hay alguna modificación en el carro de compras.

    <span id="cart-units"> </span>
    
    <script type="text/javascript">
      Bootic.load('CartUnits', { selector: '#cart-units' })
    </script>
    

    CartRules

    Establece distintas reglas al carro de compra, de no cumplirse alguna de las reglas que definamos el cliente no podrá comprar. Estas reglas tienen el propósito de homologar tus reglas de negocio al flujo de compra de tus clientes. Por ejemplo, puedes exigir la compra de 10 mínimo unidades por producto para venta mayorista. Las reglas pueden funcionar de manera simultánea.

    <script type="text/javascript">
      Bootic.load('CartRules', { minNetTotal: { value: 1000 }, minTotalUnits: { value: 4, message: "Debes tener al menos %{valor} productos en tu carro para seguir" } });
    </script>
    

    Opciones:

    Parámetro Descripción Ejemplo
    minNetTotal Obliga que el comprador tenga en su carro un valor total de compra superior a: $"x valor" minNetTotal: { value: 1000 }
    maxNetTotal Obliga que el comprador tenga en su carro un valor total de compra inferior a: $"x valor" maxNetTotal: { value: 100000 }
    minUnitsPerProduct Obliga que el comprador tenga mínimo "x" unidades del mismo producto, se pueden completar con variantes de si mismo minUnitsPerProduct: { value: 2 }
    maxUnitsPerProduct Obliga que el comprador tenga máximo "x" unidades del mismo producto, se pueden completar con variantes de si mismo maxUnitsPerProduct: { value: 20 }
    minUnitsPerVariant Obliga que el comprador tenga mínimo "x" unidades del mismo producto minUnitsPerVariant: { value: 2 }
    maxUnitsPerVariant Obliga que el comprador tenga máximo "x" unidades del mismo producto maxUnitsPerVariant: { value: 20 }
    totalUnitsMultiple Obliga que el comprador tenga un múltiplo de X unidades de variante en el carro para continuar, EJ: 4, 6 o 8 totalUnitsMultiple: { value: 2 }
    perProductMultiple Obliga que el comprador tenga un múltiplo de "x" unidades del mismo producto, EJ: 4, 6 o 8 perProductMultiple: { value: 2 }
    perVariantMultiple Obliga que el comprador tenga un múltiplo de "x" unidades de la misma variante del producto perVariantMultiple: { value: 2 }
    minTotalUnits Obliga que el comprador tenga mínimo "x" productos, pueden ser del mismo tipo o distintos minTotalUnits: { value: 4}
    maxTotalUnits Obliga que el comprador tenga máximo "x" productos, pueden ser del mismo tipo o distintos minTotalUnits: { value: 4}

    *todas las opciones necesitan a "value" como parametro requerido y no consta con un valor por defecto

    Ejemplo de reglas

    DynamicCartButtons

    {% cart_table_template %} <!-- necesario para CartModal -->
    
    <script>
      // siempre y cuando no esté cargado DynamicCartTable (que ocurre automáticamente en /cart)
      if (!Bootic.loaded('DynamicCartTable')) {
        // cargamos el módulo DynamicCartButtons
        Bootic.load('DynamicCartButtons');
        // y su complemento, el módulo CartModal
        Bootic.load('CartModal', { cartLinkSelector: '.cart-link' });
      }
    </script>
    

    "AJAXifica" los botones "Agregar al carro" de tu tienda, para que los ítemes se agreguen al carro sin la necesidad de recargar toda la página. Generalmente se usa en combinación con el módulo CartModal, para que éste muestre un modal con el carro luego de realizado el cambio.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    formSelector Selector del formulario que contiene el botón Agregar al carro No .add_to_cart

    DynamicCartTable

    La versión "dinámica" de nuestro carro de compras, y la que se usa hoy por defecto en la plantilla cart.html. Normalmente no deberías tener que usar este módulo directamente, a menos de que quieras hacer algo avanzado.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    containerId ID del contenedor No dynamic-cart-table
    templateId ID de la plantilla HTML del carro No cart-table-template
    templateName Nombre de la plantilla (para efectos del módulo Templates) No dynamic-cart-table
    productsTemplateId ID de la plantilla para el listado de productos relacionados No Ninguno related-products-template

    DynamicPrices

    Para aquellos productos que tengan variantes con distintos precios, este módulo actualiza el precio desplegado en la ficha de un producto en base al la variante que esté seleccionada.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    variantsContainer Contenedor del listado de variantes No .cart_variants
    variantSelector Selector para las variantes, dentro del contenedor No input
    priceSelector Selector que muestra el precio No .bootic-price
    priceComparisonSelector Selector para el precio de comparación No .bootic-price-comparison
    priceComparisonBoxSelector Selector para la caja que contiene precio de comparación No .bootic-price-comparison-box
    availableVariantSelector Selector para las variantes que están disponibles No .available-variant-item
    variantPriceSelector Selector para los precios de las variantes No .bootic_variant_price

    DynamicVariants

    Para productos con distintas opciones de variantes (e.g. Color, Tamaño y/o Material), este módulo despliega la matriz de variantes en forma dinámica, permitiendo que el usuario elija entre las alternativas disponibles por opción, en vez de mostrar un listado unidimensional (y largo).

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    variantsContainer Contenedor del listado de variantes No '.cart_variants
    variantList Objeto Javascript que contiene la matriz de variantes a desplegar Si
    variantOptions Objeto Javascript que contiene la matriz de opciones de variante del producto Si

    FacebookPixel

    Carga el código del Pixel de Facebook y gatilla los eventos correspondientes, dependiendo de la sección del sitio donde esté el usuario.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    pixelId Identificador de la cuenta o pixel de Facebook Ninguno 1740478256215512

    GoogleAnalytics

    Carga el código Google Analytics, con las opciones opciones necesarias para el linkeo de eventos entre la tienda y la sección de pagos (checkout).

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    analyticsId Identificador del perfil de Analytics a cargar Ninguno UA-41958709-1
    cookieDomain Dominio a asociar a la cookie de Analytics Ninguno auto o www.mitienda.com
    legacyCookieDomain Dominio a asociar a la cookie de Analytics Ninguno www.mitienda.com

    LiveSearch

    Permite mostrar resultados de búsqueda "en vivo" a medida que el usuario va escribiendo, como en Google. Internamente usa la librería FlexSearch.js.

    <form id="search-form" action="/search">
      <input id="search-input" type="text" autocomplete="off" name="q" placeholder="Buscar:" />
    </form>
    
    <script>
    Bootic.require('LiveSearch', function(search) {
      var input = document.getElementById('search-input');
      if (!input) return; // no hay input, no podemos seguir.
    
      // comenzamos el indexado de productos
      search.init(function() {
        console.log('LiveSearch indexing started.');
      }, function() {
        console.log('LiveSearch indexing finished!');
    
        // ok, el indexado está listo. ahora asociamos la instancia
        // del buscador live al input declarado arriba.
        search.bindTo(input, {
          itemRenderer: function(result) {
            // aqui podemos customizar cómo se muestran los resultados
            // eventualmente podríamos mostrar ciertos tipos de productos
            // de una manera u otros en otra, o bien filtrar algunos.
            var html = '<span>' + result.type + ' ' + result.name + '</span>';
            if (result.vendor) html += ' <em>(' + result.vendor + ')</em>';
            return html;
          }
        })
      })
    })
    </script>
    

    Métodos

    init(onIndexing, onFinished)

    Llamado para comenzar el indexado de productos. Recibe dos callbacks como argumentos. El primero, onIndexing se llama al momento en que comienza el indexado (luego de cargar las librerías necesarias y el feed de datos), y el segundo, onFinished, cuando termina el proceso de indexado.

    bindTo(input, options)

    Asocia la instancia del buscador al input pasado como primer argumento. El objeto options es opcional y contiene los parámetros que se usarán para realizar la búsqueda, cada vez que el input cambie su valor via on('input').

    Opcionalmente, el objeto options también puede contener una propiedad llamada itemRenderer asociada a una función que es la que se usará para desplegar cada resultado (ver ejemplo de arriba).

    search(query, options, callback)

    Permite realizar una búsqueda en forma "manual", en caso de que no quieras usar el método bindTo descrito aquí arriba. Los parámetros que recibe el objeto options son los siguientes:

    Opciones para search:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    limit Número de resultados máximos a entregar No 10 5

    PromoNotice

    Despliega un aviso en caso de que haya una promoción activa en el carro. Se carga por defecto dentro de bootic_footer 'v2', por lo que no es necesario que lo incluyas.

    VariantImages

    Se usa en las fichas de producto para cambiar la imagen desplegada en base a la variante elegida por el usuario. Depende de que el producto tenga más de una variante disponible, y que las imágenes hayan sido asociadas previamente a ellas.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    variantsContainer Contenedor del listado de variantes No .cart_variants
    variantSelector Selector para las variantes dentro del contenedor No input
    imageSelector Selector para las imágenes No .product-image

    InstagramImages

    <script>
      Bootic.require('InstagramImages', function (ig) {
        var options = {
          token: '21487426622.1677ed0.6248104751e9424bac7b1a5bf39788fd',
          count: 7,
          resolution: 'standard_resolution'
        }
    
        // fetch(options, callback)
        ig.fetch(options, function (images) {
          var html = images.map(function (item) {
            return '<img src="' + item.images[options.resolution].url + '" />'
          })
          $('#instagram').html(html);
        })
    
        // fetchAndRender(options, callback)
        options.container = '#instagram'
        let perPage = chooseSize() //breakpoint
        ig.fetchAndRender(options, () => {
          Bootic.load('ImageGallery', { selector: '#instagram', perPage, loop: true });
        });
      })
    
      function chooseSize(){
        if (screen.width < 800)
          return 1
        else if (screen.width < 1280)
          return 3
        else
          return 5
      }
    </script>
    

    Permite mostrar una lista de imágenes de una cuenta Instagram. Cada vez que se suba una imagen, se verá reflejada en la tienda de forma automáticamente en la sección elegida.

    Métodos

    .fetch(options, callback)

    Retorna un arreglo de objetos con la URL de la imagen y otras descripciones, como el número de likes :heart:, comentarios, etc

    .fetchAndRender(options, callback)

    Imprime la lista de imágenes. En el callback podemos llamar al módulo Bootic ImageGallery para que tome la lista de imágenes y las ponga en un carousel.

    Opciones:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    token Este token es el identificador de su Instagram, puedes obtenerlo de este sitio pixelunion
    count Número de imágenes que queremos obtener. Por ejemplo, si necesitamos las últimas 10 imágenes en nuestra lista, debemos escribir 10 8
    resolution La resolución de la imagen que Instagram nos entregará 'standard_resolution' 'thumbnail' o 'low_resolution'
    container Solo es requerido en fetchAndRender, corresponde al <div> donde se mostrará la galería de imágenes 'standard_resolution' #instagram'

    ImageGallery

    <div id="instagram">
      <div> <img src="..."> </div>
      <div> <img src="..."> </div>
      <div> <img src="..."> </div>
    </div>
    <button class="gallery-prev"> Atrás </button>
    <button class="gallery-next"> Adelante </button>
    <script> Bootic.load('ImageGallery', { selector: '#instagram', perPage: 5, loop: true }); </script>
    

    Permite ordenar una lista de imágenes en un carrusel, la librería con la cual trabajamos es Siema, por lo que con nuestra implentación solo deberás completar los parámetros de este módulo. Para moverte en el carrusel puedes definir dos clases css que son .gallery-prev y .gallery-next.

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    selector El id del div que contiene la lista de imágenes
    perPage Cantidad de imágenes visibles por página No 1
    draggable Permite avanzar en el carrusel arrastrando el mouse No true
    loop Una vez mostradas todas las imágenes estas se repetirán si loop está en true No false
    duration Duración en milisegundos de la transición del slider No 200

    RelatedProducts

    <script>
    Bootic.require('RelatedProducts', function (rp) {
      rp.get({ slug: 'camisapolo' }, (nil, products )=>{
        console.log(products)
      })
    })
    </script>
    

    Retorna una lista de productos relacionados al product.slug que recibe como parámetro

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    product un objeto con el slug del producto { slug: PoleronGap }

    ShippingCosts

    <script>
    Bootic.require('ShippingCosts', function (shipping) {
      var address = {
        region_code: "02",
        country_code: "CL",
        locality_id: "44"
      }
      shipping.getCost(address, function(val){ console.log(val) })
    })
    </script>
    

    Se implementa internamente en la etiqueta {% shipping_calculator %}

    Métodos

    .getCost(address, cb)

    Retorna un arreglo con las opciones disponibles de despacho para la zona, cada una con su valor. Los parámetros son los ID de las posibles direcciones (país, región y comuna), también toma en consideración el peso del los productos sumados en el carro de compra. El output sería algo asi ["Chilexpress, Dia habil siguiente", "$9.390"]

    .getDirections(country, region, callback)

    Retorna un arreglo de direcciones dependiendo de los parámetros que reciba, si recibe country entonces retornará las regiones del country, si recibe country y region entonces retornará las localidades del lugar, si country y region es null, retornará una lista de países.

    Opciones para getCost:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    region_code Código de la región "09"
    country_code Código del país "CL"
    locality_id Código de la localidad "44"

    Opciones para getDirections:

    Parámetro Descripción Requerido Valor por defecto Ejemplo
    country Código del país No "CL"
    region Código de la región No "09"

    Ejemplos y trucos

    Para desplegar los links de un menú en específico dentro de tu plantilla, debes usar la manilla desde la variable menus e iterar sus links, en la forma menus.MANILLA.links. Ejemplo:

      <ul class="navegacion">
      {% for link in menus.navegacion-footer.links %}
        <li class="{{ link.slug }}{{ ' current' | print_if_equal: resource, link }}">
          <a href="{{ link.url }}">{{ link.title }}</a>
        </li>
      {% endfor %}
      </ul>
    

    El atributo slug en cada link es el valor de su propia manilla. En este caso lo usamos para asignar clases CSS a cada item del menu.

    Además usamos el helper print_if_equal para agregar la clase "current" si un link es igual a la página actual (resource).

    De esta forma puedes crear y mostrar distintos menús de navegación en tus páginas.

    En Bootic los menús son todos "de primer nivel", es decir, no se puede marcar un menú como "de segundo nivel" (un submenú) desde la interfaz de administración, pero sí es posible desplegarlas de esta forma en las plantillas.

    Para esto usamos un truco de nuestra implementación de Liquid, que permite ver si existe un menú llamado de cierta forma, pero todo con variables. Es decir, si creamos un menú llamado "Navegación Principal", que tiene un link llamado "Catálogo", y al mismo tiempo creamos otro menú con el mismo nombre "Catálogo", podemos hacer lo siguiente:

    <ul>
    {% for link in menus.navegacion-principal.links %}
      <li class="{{ ' current' | print_if_equal: resource, link }}">
        <a href="{{ link.url }}">{{ link.name }}</a>
    
        <!-- ahora revisamos si existe un menú con el mismo nombre que el link -->
        {% if menus[link.name] %}
          <!-- si es así, iteramos sobre sus links para crear el submenú -->
          <ul class="submenu">
          {% for sublink in menus[link.name] %}
            <li>
              <a href="{{ sublink.url }}">{{ sublink.name }}</a>
            </li>
          {% endfor %}
          </ul>
        {% endif %}
    
        <!-- y ahora seguimos con el resto de los links -->
      </li>
    {% endfor %}
    </ul>
    

    Ejemplo de submenú

    Una segunda opción para generar submenús es usar tipos de productos dentro de colecciones. En este caso, si uno de los links de un menú apunta hacia una colección, podemos verificar si esa colección contiene productos de distintos tipos, y en base a eso generar links automáticamente:

    <ul>
    {% for link in menus.navegacion-principal.links %}
      <li class="{{ ' current' | print_if_equal: resource, link }}">
        <a href="{{ link.url }}">{{ link.name }}</a>
    
        <!-- en este caso, en vez de revisar si existen otros menus con el mismo
             nombre del link, revisamos si el link contiene tipos de producto -->
        {% if link.has_product_types %}
          <ul class="submenu">
          {% for type in link.product_types %}
            <li>
              <a href="{{ link | typed_collection_path: type }}">{{ type.name }}</a>
            </li>
          {% endfor %}
          </ul>
        {% endif %}
    
      </li>
    {% endfor %}
    </ul>
    

    Galería o slideshow de productos

    Los "banners" o imágenes rotativas de productos pueden ser resueltos utilizando las galerías.

    galeria

    En este ejemplo, vamos a construir un slideshow o 'slider' con las imágenes contenidas en una galería Bootic. Para esto usaremos el plugin Owl Carrousel, que a su vez depende de jQuery (aunque puedes usar cualquier librería para esto, como Glide.js, Siema.js u otra).

    El primer paso es crear una galería llamada "Portada" desde el panel de administración y agregar al menos dos imágenes.

    El código

    Este es la estructura de HTML que necesita Owl Carrousel para funcionar. Vamos a alimentarlo de las imágenes que hayan en la galería portada. Inclúyelo en la portada de tu tienda (home.html):

    <div class="owl-carousel owl-theme">
    {% for item in galleries.portada %}
      <div>
        <a href="{{ item.url }}">
          <img src="{{ item.image | resize: '540x' }}" />
        </a>
      </div>
    {% endfor %}
    </div>
    

    A continuación es necesario incluír las librerías JavaScript jQuery y el plugin Cycle. En layout.html, en la sección head:

    (...)
    <!-- Hojas de estilo necesarias para el carrusel de portada (galeria) -->
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/assets/owl.carousel.min.css">
    <link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/assets/owl.theme.default.css">
    

    Luego agregamos el código JS necesario también en layout.html, pero abajo, justo antes del cierre de la etiqueta </body>.

    <!-- librerías necesarias para el carrusel de portada (galeria) -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery/2.0.0/jquery.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/jquery-migrate/1.4.0/jquery-migrate.js"></script>
    <script src="//cdnjs.cloudflare.com/ajax/libs/OwlCarousel2/2.0.0-beta.3/owl.carousel.min.js"></script>
    
    <!-- llamada -->
    <script type="text/javascript">
      $(function() {
        $('.owl-carousel').owlCarousel({
          items      : 1,
          margin     : 10,
          autoHeight : true
        });
      })
    </script>
    

    ¡Listo! Visita la portada de tu sitio o recarga la página, y deberías poder ver la galería funcionando.

    Widget embebible de productos

    Bootic cuenta con un widget Javascript que permite embeber y mostrar una lista de productos fuera de una tienda Bootic, ya sea en un sitio Wordpress o similar.

    Para esto es necesario incluir el siguiente script tag:

    <script src="//du8eo9nh88b2j.cloudfront.net/libs/0.0.2/search_widget.min.js"></script>
    

    Ojo que el tag requiere de jQuery, así que en caso de que no esté disponible debes incluirlo antes:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    

    Y luego incluir una etiqueta HTML vacía con un par de opciones:

    <ul class="bootic-products-widget"
      data-bootic_widget="ProductSearch"
      data-template="product-list"
      data-config_per_page="12"
      data-config_shop_subdomains="mitienda"
      data-autoload_collections="ofertas"
      data-autoload="true">
    </ul>
    

    Estos parámetros definen cómo se comporta el widget. El más importante es data-config_shop_subdomains, que corresponde al subdominio asociado a tu tienda (el que pusiste al momento de crearla, este caso mitienda.bootic.net). data-autoload_collections define qué colecciones mostrar, y data-config_per_page el número de resultados por página.

    El atributo data-template corresponde al nombre de la plantilla HTML que usaremos para desplegar los productos, que en este caso está definido como product-list. Esto quiere decir que el widget buscará en la página una etiqueta <script type="text/html"> que tenga el atributo data-template definido como product-list. Algo así:

    <script type="text/html" data-template="product-list">
      %{items}
      <li>
        <h4>%{title}</h4>
        %{hasImage}
          <img src="%{image.sizes.small}" />
        %{/hasImage}
        <p>$%{formatted_price}</p>
        <a href="%{href}" target="_blank">Comprar</a>
      </li>
      %{/items}
    </script>
    

    Si juntamos todo este código, se vería así:

    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.4.1/jquery.min.js"></script>
    <script src="//du8eo9nh88b2j.cloudfront.net/libs/0.0.2/search_widget.min.js"></script>
    
    <ul class="bootic-products-widget"
      data-bootic_widget="ProductSearch"
      data-template="product-list"
      data-config_per_page="12"
      data-config_shop_subdomains="mitienda"
      data-autoload_collections="ofertas"
      data-autoload="true">
    </ul>
    
    <script type="text/html" data-template="product-list">
      %{items}
      <li>
        <h4>%{title}</h4>
        %{hasImage}
          <img src="%{image.sizes.small}" />
        %{/hasImage}
        <p>$%{formatted_price}</p>
        <a href="%{href}" target="_blank">Comprar</a>
      </li>
      %{/items}
    </script>
    

    Ahora, al recargar la página el widget desplegará los primeros 12 productos de mitienda asociadas a la colección ofertas. Así:

    <ul class="bootic-products-widget"
      data-bootic_widget="ProductSearch"
      data-template="product-list"
      data-config_per_page="12"
      data-config_shop_subdomains="mitienda"
      data-autoload_collections="featured"
      data-autoload="true">
    
        <li>
          <h4>VIÑAMAR UNIQUE 750</h4>
          <img src="https://r.btcdn.co/3358/small/494080-1452060.jpg">
          <p>$4.820</p>
          <a href="https://liquidos.bootic.net/products/vinamar-unique-750" target="_blank">Comprar</a>
        </li>
    
        <li>
          <h4>VALDIVIESO BRUT 750</h4>
          <img src="https://r.btcdn.co/3358/small/494080-1452060.jpg">
          <p>$3.390</p>
          <a href="https://liquidos.bootic.net/products/valdivieso-brut-750" target="_blank">Comprar</a>
        </li>
    
        (...)
    </ul>
    

    Botones de redes sociales en ficha de un producto

    Redes sociales como Twitter y Facebook ofrecen botones en formato HTML que puedes pegar en tu propio sitio. Esta página muestra el procedimiento básico para usar los botones de Twitter y Facebook en las plantillas HTML de tu sitio. Necesitarás conocimientos básicos de HTML y también algo de CSS si quieres ajustar el diseño y posición de los botones.

    En Bootic, puedes pegar estos botones en la plantilla HTML de cada producto. Esta plantilla, llamada product.html, la encuentras en el editor en línea de la interfaz de administración de tu tienda.

    Template de producto

    Botón Twitter

    El código del botón de Twitter lo encuentras en esta dirección: http://twitter.com/about/resources/tweetbutton.

    Ahí puedes personalizar el diseño y texto y generar el código HTML.

    Twitter button

    A continuación debes copiar el código y pegarlo en el lugar apropiado de tu plantilla product.html en el editor Bootic. En este ejemplo, usando la plantilla "base" de Bootic, lo situaré a la derecha del botón para agregar al carro de compra.

    Twitter code in editor

    Salvamos los cambios en el editor y vamos a cualquier página de producto en nuestro sitio.

    Twitter example

    Botón "like" de Facebook

    El botón "Like" de Facebook requiere algo de configuración. Facebook también tiene una página donde puedes cambiar aspectos del botón y generar el código HTML.

    http://developers.facebook.com/docs/reference/plugins/like/

    El único campo necesario es la "URL" que quieres que tus usuarios compartan con sus amigos. En este ejemplo queremos que los usuarios compartan la dirección de cada producto. Para esto tenemos que usar una etiqueta especial de Bootic en la plantilla product.html. Por ahora pondré el texto "URL-PRODUCTO" en Facebook y lo reemplazaré con la etiqueta {{ product.url }} en el editor de Bootic.

    Facebook like button

    Presiona "get the code" y copia el código de tu botón Facebook de la opción "iframe".

    Facebook get the code

    Ahora pegamos el código en la plantilla "product.html", igual que en el ejemplo anterior junto al botón del carro de compra.

    Una vez en el editor reemplazo el texto "URL-PRODUCTO" que puse antes por la etiqueta especial de Bootic que genera la URL de cada producto, {{ product.url }}.

    Facebook editor

    Finalmente guardamos los cambios y nos aseguramos de que las páginas de producto muestran el botón adecuadamente.

    Facebook Like button

    Listo! Ahora, cada vez que alguien apriete el botón "Like" en tus páginas de productos, tus seguidores y sus amigos verán la primera imagen y título del producto en sus "walls".

    Facebook imagen de producto

    Puedes aprender más sobre todas las posibilidades de configuración del botón de Facebook en su documentación.

    Lista de productos destacados

    <ul id="productos_destacados">
      {% for product in collections.destacados.products %}
      <li>
        <!-- 1ra imagen -->
        <a href="{{ product.url }}" class="product-image">
          <img src="{{ product.first_image.small }}" alt="{{ product.model }}" />
        </a>
        <!-- título -->
        <h3 class="product-model">
          <a href="{{ product.url }}">{{ product.model }}</a>
        </h3>
      </li>
      {% endfor %}
    </ul>
    

    Crear colección destacados

    Una forma común de mostrar destacados en la portada es la siguiente:

    Ahora puedes escribir el código (en home.html) que recorra los productos dentro de la colección "detacados" y muestre sus títulos (con link) y primera imagen.

    Mostrando productos similares

    En Bootic, los productos similares (también llamados sustitutos) son aquellos que comparten el mismo Tipo de producto, y la mayor cantidad de tags en común.

    Para mostrar un bloque de productos similares en la ficha de un producto usamos similar_products y has_similar_products:

    <!-- product.html -->
    {% if product.has_similar_products %}
      <h2>Productos similares</h2>
    
      <ul class="products">
        {% loop product.similar_products in 'product_item' limit:3 %}
      </ul>
    {% endif %}
    

    Para el ejemplo anterior limitamos la lista a los primeros tres elementos del conjunto similar_products (aquellos que tengan la mayor similitud con el producto desplegado). Además usamos el bloque de codigo reutilizable product_item.

    Ejemplo

    A continuación te mostramos una página detalle de un producto, con sus similares y relacionados.

    Detalle de producto Bootic, con relacionados y similares

    Mostrando productos relacionados

    En Bootic, los productos relacionados (también llamados complementado) son aquellos que comparten la mayor cantidad de etiquetas con respecto a otro producto, y que no pertenece al mismo Tipo de producto.

    Para mostrar un bloque de productos similares en la ficha de un producto usamos related_products y has_related_products:

    {% if product.has_related_products %}
      <h2>Productos relacionados</h2>
    
      <ul class="products">
        {% loop product.related_products in 'product_item' limit:3 %}
      </ul>
    {% endif %}
    

    Para este ejemplo limitamos la lista a los primeros tres elementos del conjunto related_products (aquellos que tengan la mayor similitud con el producto desplegado). Además usamos el bloque de codigo reutilizable product_item.

    Ejemplo

    A continuación te mostramos una página detalle de un producto, con sus similares y relacionados.

    Detalle de producto Bootic, con relacionados y similares

    Sugiere productos relacionados en el carro de compra

    El siguiente trozo de código sugerirá productos relacionados justo después que el comprador agrega un producto al carro (Figura 2). Un excelente momento para incentivar otra compra.

    <!-- cart.html -->
    {% if related_products_to_just_added %}
      <h2>También te puede interesar</h2>
    
      <ul class="products">
        {% loop related_products_to_just_added in 'product_item' limit:3 %}
      </ul>
    {% endif %}
    

    Usando y mostrando atributos de un producto

    Los atributos son una buena forma de agregarle información personalizada a un producto.

    Por defecto, las plantillas Bootic muestran todos atributos del producto, siempre y cuando estos tengan un valor. Un ejemplo de cómo mostrar todos los atributos (que no estén vacíos) de un producto, sería:

    <ul class="product-attributes">
      {% for field in product.attributes.present %}
      <li class="attribute-{{field.key}}">
        <strong>{{ field.name }}:</strong> <span>{{ field.value }}</span>
      </li>
      {% endfor %}
    </ul>
    

    En caso de que quieras hacer algo especial según la presencia de cierto atributo, puedes hacer algo como esto:

    {% for field in product.attributes %}
      {% if field.key == "color" %}
        <div class="color" style="background-color: {{ field.value }}">&nbsp;</div>
      {% endif %}
    {% endfor %}
    

    Si quieres mostrar un atributo en particular (como por ejemplo el código embebido de un video) el trozo de código liquid sería así:

    {% if product.attributes.video.has_value %}
      <div class="video">
        {{ product.attributes.video.value }}
      </div>
    {% endif %}
    

    El método has_value en cada atributo revisa que el atributo exista y su valor no esté vacío.

    Calendario de noticias

    <ul id="blog-archive">
      {% for date in blog.calendar %}
      <li>
      {% capture localized_month %}datetime.month_names.month_{{date.month_num}}{% endcapture %}
        <a href="{{ date.url }}" title="{{ localized_month | t }}">
          {{ localized_month | t }} {{ date.year}} ({{ date.post_count }})
        </a>
      </li>
      {% endfor %}
    </ul>
    

    Con el objeto blog.calendar y una combinación de trucos Liquid podemos generar una lista de meses, cada uno con la cuenta de artículos para cada més. Por último generamos un link a cada mes para ver el archivo de artículos.

    El método capture es parte de Liquid y permite "capturar" el valor de un bloque de código y asignarlo a una variable, en este caso localized_months. (ver documentación Liquid)