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:
- Liquid 101 (introducción a Liquid)
- Variables globales y locales
- Estructura de plantillas
- Bloques html custom
- Etiquetas Bootic
- Archivos e imágenes de apoyo (assets)
- Bootic.js y los módulos Javascript
- 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.
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:
- una cadena de texto, como el nombre de un producto o una URL;
- un número, como el precio o peso de un producto;
- un conjunto de elementos, como un listado de productos en oferta; o bien,
- un objeto que a su vez contiene otras variables, como
shop
(que contieneshop.name
yshop.url
, dentro de otros).
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:
shop
template
yview
current_url
ycurrent_page
settings
(parámetros de la plantilla)
Menús, links y galerías:
menus
links
galleries
Productos y sus agrupaciones:
all_products
collections
product_types
vendors
tags
Contenido estático:
pages
forms
blog
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:
resource.name
resource.slug
resource.url
El objeto shop
Disponible a través de la variable global shop
.
shop.name
: Nombre de la tiendashop.description
: Descripción de la tienda, modificable en la sección de preferencias.shop.url
: URL de la portada de la tienda.shop.subdomain
shop.product_attribute_list
shop.variant_names
shop.logo
(yshop.logo.small
,shop.logo.tiny
)
El objeto menu
Disponible a través de la variable global menus
, ya sea iterando o usando un handle (ej. menus.sidebar
).
menu.title
(omenu.name
)menu.slug
menu.links
menu.has_links
El objeto gallery
gallery.title
(ogallery.name
)gallery.slug
gallery.items
: Conjunto degallery_item
gallery.has_items
gallery.size
(igual agallery.items.size
)
El objeto link
Disponible al iterar sobre el atributo links
de un objeto menu
.
link.title
(olink.name
)link.slug
link.url
: URL completa a la que apunta el link.link.path
: Ruta a la que apunta el link. Si la URL es externa, devuelve un string vacío.link.linkable
(objeto al que está asociado el link, ej. una colección o tipo de producto)link.menu
También responde a los siguientes métodos, dependiendo del tipo de objeto que esté asociado al link.
link.has_product_types
link.product_types
link.has_tags
link.tags
link.has_products
link.products
El objeto gallery_item
gallery_item.title
(ogallery_item.name
)gallery_item.slug
gallery_item.url
: URL completa a la que apunta el item de galeríagallery_item.path
: Ruta a la que apunta el item de galería. Si la URL es externa, devuelve un string vacío.gallery_item.description
: Descripción de la imagen, en caso de tenergallery_item.gallery
: Objeto de tipogallery
(por defecto imprime el slug de la galería)gallery_item.image
: Objeto de tipoasset
(imagen)
{% 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
).
collection.title
ocollection.name
: Título o nombre de la coleccióncollection.slug
: Manilla del recurso (único)collection.url
: URL completa de la coleccióncollection.body
: Descripción de la colección, en caso de tenercollection.tags
collection.product_types
collection.vendors
collection.products
: Listado de productos de la coleccióncollection.has_products
collection.first_product
collection.size
(igual aproducts.size
)collection.attributes
collection.has_attributes
El objeto product_type
Disponible iterando sobre la variable global product_types
o como el atributo de un producto
.
product_type.name
product_type.slug
product_type.url
product_type.products
product_type.has_products
product_type.first_product
product_type.vendors
product_type.tags
El objeto vendor
Disponible al iterar sobre el conjunto global vendors
, o como el atributo vendor
de un producto
.
vendor.name
: Nombre de la marca o fabricante.vendor.slug
vendor.url
: URL que despliega los productos de ese vendor.vendor.products
: Productos para ese vendor.vendor.has_products
vendor.first_product
vendor.tags
El objeto tag
tag.name
tag.url
tag.products
tag.has_products
tag.first_product
El objeto product
Disponible en la plantilla product.html
o dentro de un loop de productos.
product.model
: El nombre o modelo, sinónimo deproduct.name
yproduct.title
product.slug
: Manilla única del recursoproduct.url
: URL completa hacia el productoproduct.description
: La descripciónproduct.stock
product.has_unlimited_stock
product.is_bundle
: El producto es un pack, posibles valores [true|false]product.bundle_items
: Lista debundle_items
Conjuntos o objectos relacionados:
product.collections
: Lista de colecciones del productoproduct.has_collections
product.vendor
: La marca o fabricanteproduct.tags
: Lista de etiquetas del producto
Variantes:
product.has_variants
: [true|false]product.variants
: lista de variantes del productoproduct.variants_count
product.any_available
: [true|false] si el producto tiene alguna variable disponibleproduct.available_variant_id
product.available_variants
: lista de variantes con stock disponibleproduct.unavailable_variants
: lista de variantes con stock agotadoproduct.has_variant_options
product.variant_options_matrix
Precios:
product.price
: precio del productoproduct.price_comparison
: precio de comparación del productoproduct.price_comparison_percentage
product.has_price_comparison
: [true|false]product.has_multiple_prices
: [true|false]
Imágenes asociadas:
product.images
: lista de imágenes asociadas al productoproduct.has_images
: [true|false]product.first_image
: primera imagen del productoproduct.image_count
Archivos asociados:
product.files
: lista de archivos (no imágenes) del productoproduct.has_files
: [true|false]product.first_file
product.file_count
Tipo y atributos:
product.product_type
: tipo de producto con campos personalizadosproduct.attributes
: Atributos personalizados de productoproduct.has_attributes
product.weight_in_gr
: peso del producto, en gramosproduct.weight_in_kg
: peso del producto, en kilos
Productos similares o relacionados:
product.has_similar_products
: [true|false]product.similar_products
product.has_related_products
: [true|false]product.related_products
El objeto variant
Disponible al iterar sobre el conjunto variants
un producto.
variant.title
(ovariant.name)
: Nombre de la variante.variant.sku
: SKU de esta variante.variant.weight_in_grams
variant.related_assets
Variables relacionadas al stock:
variant.stock
: Cantidad de unidades en stock.variant.is_available
: True o false dependiendo de si hay unidades en stock.variant.is_available_if_no_stock
variant.has_unlimited_stock
variant.option_values
Variables relacionadas al precio:
variant.price
variant.price_comparison
variant.price_comparison_or_nil
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.
bundle.product_id
bundle.variant_id
bundle.name
bundle.units
bundle.customizable
: [true|false]
El objeto asset
Contiene las siguientes variables:
asset.url
: URL hacia el archivo originalasset.extension
: Extensión del archivo (png, gif, pdf, xls, etc)asset.file_name
: Nombre original del archivoasset.title
: Título opcional asignado al subirasset.description
: Descripción opcional asignada al subirasset.related_variant_id
: ID de la variante asociada, en caso de tener una
Variables específicas para imágenes:
asset.thumbnail
: Ruta hacia la versiónthumbnail
(75x75) de la imagen (no aplica a otros archivos)asset.small
: Ruta hacia la versiónsmall
(240x180) de la imagen (no aplica a otros archivos)asset.medium
: Ruta hacia la versiónmedium
(500x375) de la imagen (no aplica a otros archivos)asset.large
: Ruta hacia la versiónlarge
(800x600) de la imagen (no aplica a otros archivos)asset.original
: Ruta hacia la versión original de la imagen (igual queurl
)
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
).
page.title
: El título dado a la página en la interfaz de administración.page.body
: El contenido completo de la página, incluyendo links, imágenes y HTML que le hayas agregado en el editor de páginas en la interfaz de administración.page.url
: URL hacia la página.page.attributes
page.has_attributes
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
).
form.title
form.body
form.url
form.attributes
form.has_attributes
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:
blog.posts
blog.posts_count
blog.url
blog.date
(blog.year
,blog.month
,blog.day
)blog.recent
blog.calendar
El objeto post
Disponible en la plantilla post.html o al iterar sobre un conjunto de posts (blog.posts
). Tiene las siguientes variables:
post.title
post.url
post.body
post.published_on
post.previous
post.next
post.user_name
post.user_email
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:
/products
: Aquí se muestran todos los productos visibles de la tienda, ordenados según el criterio general que tengas definido en las preferencias de tu tienda/collections/destacados
: Todos los productos visibles que pertenezcan a la colección "Destacados", ordenados según el criterio propio de la colección (en caso de haber), o sino el de la tienda,/types/zapatillas
: Todos los productos visibles que sean de tipo Zapatillas, ordenados según el criterio de la tienda./vendors/puma
: Todos los productos visibles de marca Puma, ordenados según el criterio de la tienda./tags/nuevo
: Todos los productos visibles etiquetados como "nuevo", ordenados según el criterio de la tienda.
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:
- Colección + Tipo (
/collections/destacados/types/zapatillas
) - Tipo + Fabricante (
/types/zapatillas/vendors/puma
) - Fabricante + Tag (
/vendors/puma/tags/nuevo
) - Colección + Fabricante (
/collections/destacados/vendors/puma
) - Tipo + Tag (
/types/zapatillas/tags/nuevo
) - Colección + Tag (
/collections/destacados/tags/nuevo
) - Tipo + Fabricante (
/types/zapatillas/vendors/puma
)
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:
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
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.
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:
Y así es cómo se ve el resultado final:
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:
- Foto y nombre del producto
- Precio unitario
- Texto de ingreso con la cantidad de productos
- Subtotal del precio unitario por la cantidad de productos
- Enlace para borrar la fila
Además mostrará 3 botones:
- Seguir comprando (volverá al portada de la tienda)
- 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:
{% if %}
y{% endif %}
{% unless %}
y{% endunless %}
{% for %}
y{% endfor %}
{% comment %}
{% case %}
{% cycle %}
{% assign %}
{% capture %}
{% include [template] %}
Si quieres leer más sobre ellas, te recomendamos visitar la documentación oficial de Liquid.
Etiquetas Bootic generales:
{{ content_for_layout }}
{% current_title %}
{% current_description %}
{% navigation_path %}
{% bootic_header [version] %}
y{% stripped_bootic_header %}
{% bootic_footer [version] %}
{% loop [objects] in 'template' %}
{% paginate [list] by [num_per_page] %}
Etiquetas Bootic para carro y productos:
{% unit_price %}
y{% unit_price_for [product] %}
{% stock_status %}
y{% stock_status_for [product] %}
{% product_filters %}
{% add_to_cart %}
y {% add_to_cart_for [product] %}{% add_to_cart_no_variants %}
{% cart %}
y{% cart_table %}
{% cart_table_template %}
{% dynamic_cart_table %}
{% shipping_calculator %}
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.
{% bootic_footer [version] %}
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.
{% 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)
to_slug(string)
format_text(string)
split_text(string)
print_if_equal(string, one_string, two_string)
print_if_matches(string, one_string, two_string)
print_if_included(string, needle, haystack)
escape_html(string)
json_escape(string)
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
paginate(array)
(Ver sección sobre paginación)size(array)
join(array, segmenter = ' ')
first(array)
last(array)
Filtros de URLs
if_current_page(string, link)
if_within_section(string, link)
if_within_catalog(string)
asset_url(filename)
yasset_url_no_timestamp(filename)
image_tag(url[, alt_text])
javascript_tag(filename)
yasync_javascript_tag(filename)
stylesheet_tag(filename)
Filtros de URLs de colecciones
Ejemplo de uso:
{{ collection | typed_collection_path:type }}
tagged_resource_path(resource, tag)
typed_collection_path(collection, type, page = 1)
tagged_collection_path(collection, tag, page = 1)
vendored_collection_path(collection, vendor, page = 1)
tagged_typed_products_path(type, tag, page = 1)
vendored_typed_products_path(type, vendor, page = 1)
tagged_vendored_products_path(vendor, tag, page = 1)
vendored_products_path(vendor, page = 1)
Filtros de imágenes
resize(image, geometry)
greyscale(image)
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.
Para insertar una hoja de estilos primero tenemos que crear una plantilla de tipo "css" y darle un nombre.
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
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 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
.
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
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 <img>
. 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:
thumbnail
: 75x75small
: 240x180medium
: 500x375large
: 800x600
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:
'400x300'
: tamaño personalizado, mantiene proporciones.'400x300!
: fuerza tamaño, no mantiene proporciones.'400x'
: ancho personalizado, mantiene proporciones.'x300'
: alto personalizado, mantiene proporciones.'400x300>
: modifica tamaño sólo si imagen es más grande que esto'400x300<
: modifica tamaño sólo si imagen es más pequeña que esto'50x50%
: modifica alto y ancho al %50'400x300^
: modifica ancho, alto a mínimo 400x300. Mantiene proporciones.'2000@
: modifica para que area máxima sea 2000 pixeles'400x300#
: modifica, recorta si es necesario para mantener proporciones. Centra.'400x300#ne
: modifica, recorta si es necesario para mantener proporciones. Ajusta arriba, derecha.'400x300#nw
: modifica, recorta si es necesario para mantener proporciones. Ajusta arriba, izquierda.'400x300#se
: modifica, recorta si es necesario para mantener proporciones. Ajusta abajo, derecha.'400x300#sw
: modifica, recorta si es necesario para mantener proporciones. Ajusta abajo, izquierda.'400x300+50+100
: recorta desde el punto 50, 100 con ancho y alto de 400x300
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.
q
: termino de busqueda (ej: mousepad)price_range
: rango de precio (ej: 10000-20000 o bien 10000- o -20000)collections
: arreglo de slugs de colecciones para filtrarproduct_types
: arreglo de slugs de tipos de producto para filtrartags
: arreglo de slugs de etiquetas para filtrarvendors
: arreglo de slugs de marcas para filtrarattributes
: objeto { key: value } con atributos de producto y sus valoresvariants
: arreglo de nombres de variante (ej: "Blanco", "Azul")sort
: orden de productos, puede sertitle.asc
,price.asc
,price.desc
,discount_percentage.asc
, odiscount_percentage.desc
page
: número de página a buscar. se complementa con el parámetro per_page de la etiqueta paginate
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)
- CartUnits
- CartRules
- CartModal
- DynamicCartButtons
- DynamicCartTable
- DynamicPrices
- DynamicVariants
- FacebookPixel
- GoogleAnalytics
- LiveSearch
- PromoNotice
- VariantImages
- InstagramImages
- ImageGallery
- RelatedProducts
- ShippingCosts
¿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 div
s 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.
Modal
<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 usarBootic.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 | Sí | 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
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 | Sí | 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 | Sí | Ninguno | UA-41958709-1 |
cookieDomain | Dominio a asociar a la cookie de Analytics | Sí | Ninguno | auto o www.mitienda.com |
legacyCookieDomain | Dominio a asociar a la cookie de Analytics | Sí | 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 | Sí | ||
count | Número de imágenes que queremos obtener. Por ejemplo, si necesitamos las últimas 10 imágenes en nuestra lista, debemos escribir 10 | Sí | 8 | |
resolution | La resolución de la imagen que Instagram nos entregará | Sí | 'standard_resolution' |
'thumbnail' o 'low_resolution' |
container | Solo es requerido en fetchAndRender, corresponde al <div> donde se mostrará la galería de imágenes |
Sí | '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 | Sí | ||
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 | Sí | { 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 | Sí | "09" | |
country_code | Código del país | Sí | "CL" | |
locality_id | Código de la localidad | Sí | "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
Menús y submenús de navegación
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.
Submenús o menús desplegables
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>
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.
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.
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.
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.
Salvamos los cambios en el editor y vamos a cualquier página de producto en nuestro sitio.
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.
Presiona "get the code" y copia el código de tu botón Facebook de la opción "iframe".
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 }}
.
Finalmente guardamos los cambios y nos aseguramos de que las páginas de producto muestran el botón adecuadamente.
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".
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>
Una forma común de mostrar destacados en la portada es la siguiente:
- Crea una colección llamada "detacados" (si es que no existe una ya)
- Agrega los productos que quieras a la colección.
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
yhas_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.
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
yhas_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.
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 }}"> </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)