13 KiB
Builder Fields & Acai Attributes
Nombres de variables
El atributo data-field-label se convierte a variable removiendo espacios y caracteres especiales (minúsculas).
| Label | Variable |
|---|---|
| Categoría Noticia | categoranoticia |
| Color Principal | colorprincipal |
| Título Producto | ttuloproducto |
Field Types (data-field-type)
| Type | Element | Returns |
|---|---|---|
textfield |
<p> |
String |
headfield |
<h1>-<h6> |
String + variable _tag con la etiqueta elegida |
textbox |
<div> |
String multi-línea |
wysiwyg |
<div class="wysiwyg"> |
HTML string |
link |
<a> |
URL string (ya incluye barras) |
upload |
<img> |
Array de {urlPath, info1, info2, info3, info4} |
uploadMulti |
<li> |
Itera sobre archivos subidos |
list (fijo) |
<div data-list-options="..."> |
Valor seleccionado |
list (tabla) |
<div data-list-table="..."> |
num del registro |
multiv2 |
<li> wrapper |
Array de objetos |
textfield
<p data-field-type="textfield" data-field-label="Título">
Elemento editable
</p>
headfield
Genera 2 variables: la estándar y otra con sufijo _tag con la etiqueta elegida por el usuario.
<{{ title_tag | default('h2') }} data-field-type="headfield" data-field-label="Título Sección" class="text-3xl font-bold">
Título de la sección
</{{ title_tag | default('h2') }}>
textbox
<div data-field-type="textbox" data-field-label="Descripción">
Texto largo editable
</div>
wysiwyg
<div class="wysiwyg" data-field-type="wysiwyg" data-field-label="Contenido Enriquecido">
<p>Texto con <strong>estilos</strong> editables</p>
</div>
link
<a data-field-type="link" data-field-label="Enlace Principal" href="#">
Haz clic aquí
</a>
upload
<div class="p-1/6 relative">
<img
class="absolute top-0 left-0 w-full h-full object-cover object-center lazyload"
data-field-type="upload"
data-field-label="Imagen Principal"
data-lazy="true"
data-field-info1="titulo"
data-field-width="1400"
alt=""
>
</div>
Atributos disponibles:
data-lazy="true": Carga perezosadata-field-width="1400": Ancho máximo sugeridodata-field-info1="titulo": Campo de información adicional (usado como alt)
Acceso en Twig: {{ imagen[0].urlPath }}, {{ imagen[0].info1 }}
uploadMulti
Itera sobre todas las imágenes subidas:
<li data-field-type="uploadMulti" data-field-label="Galería" data-field-info1="titulo">
<div class="relative min-h-screen">
<img class="absolute top-0 left-0 w-full h-full object-cover lazyload"
data-src="{{ uploadMulti.urlPath | imagec(2100) }}"
alt="{{ uploadMulti.info1 }}">
</div>
</li>
list (opciones fijas)
<div
data-field-type="list"
data-field-label="Color Producto"
data-list-options="Rojo,Azul,|Verde,3|Amarillo"
>
</div>
Formato de opciones: opcion1,opcion2,|opcion3,valor3|opcion4
list (tabla)
<div
data-field-type="list"
data-field-label="Noticia Destacada"
data-list-table="noticias"
data-list-value="num"
data-list-label="titulo"
>
{{ record.titulo }}
</div>
data-list-table: Nombre de tabla sin prefijocms_data-list-value: Campo a usar como valor (generalmentenum)data-list-label: Campo a mostrar como label
multiv2 — Campos repetibles
<ul>
<li data-field-type="multiv2" data-field-label="Productos">
<div data-field-type="textfield" data-field-label="Nombre">
Nombre del producto
</div>
<div data-field-type="textbox" data-field-label="Descripción">
Descripción del producto
</div>
<div class="p-1/6 relative">
<img
class="absolute top-0 left-0 w-full h-full object-cover lazyload"
data-field-type="upload"
data-field-label="Imagen"
data-lazy="true"
data-field-width="800"
alt=""
>
</div>
</li>
</ul>
Uso en Twig — las variables son propiedades del objeto iterado:
{% for record in productos %}
<div class="producto">
<h3>{{ record.nombre }}</h3>
<p>{{ record.descripcion }}</p>
<img src="{{ record.imagen[0].urlPath }}" alt="">
</div>
{% endfor %}
Acai Attributes
c-if — Renderizado condicional
<!-- Verificar existencia de variable -->
<div c-if="subtitle">{{ subtitle }}</div>
<!-- Comparación de valores (usa = no ==) -->
<div c-if="layout = 'grid'">Grid layout</div>
c-else
Debe ir inmediatamente después del elemento c-if:
<div c-if="image">
<img src="{{ image[0].urlPath }}" />
</div>
<div c-else>
<p>No image available</p>
</div>
c-for — Iteración sobre array
<div c-for="item in record.features">
<h3>{{ item.title }}</h3>
</div>
c-for — Iteración sobre tabla de BD
<ul>
<li c-for="producto in productos" c-where="'visible=1'" c-order="'num desc'" c-limit="10">
{{ producto.title }}
</li>
</ul>
Parámetros opcionales: c-where (condición SQL), c-order (orden), c-limit (límite).
Equivalente en Twig:
{% for producto in 'productos' | get('visible=1','num desc',10) %}
<li>{{ producto.title }}</li>
{% endfor %}
Dentro del loop: loop.index (1-based), loop.index is odd, loop.index is even
c-class — Clases CSS condicionales
<!-- Simple -->
<div c-class="{ 'text-center': alineacion == '1', 'text-right': alineacion == '2' }">
<!-- Múltiples condiciones -->
<div c-class="{
'flex-row-reverse': orden == '1',
'cursor-pointer click-a-child': record.enlace_anchor,
'rounded-xl': radioborde == '4'
}">
<!-- Con expresiones Twig (loop) -->
<div c-class="{
'md:order-1': loop.index is odd,
'md:pl-6': loop.index is even
}">
<!-- Combinado con clases estáticas -->
<div class="flex items-center" c-class="{ 'justify-center': centrado }">
c-hidden — Elementos ocultos
Elemento que no se renderiza pero puede declarar variables builder:
<div c-hidden="true">
<input data-field-type="textfield" data-field-label="Config" value="default" />
</div>
c-required — Campos requeridos condicionales
<input type="text" name="telefono" c-required="'2' not in camposquitar" placeholder="Teléfono">
Definiendo variables con <set>
<!-- Obtener configuración de la BD -->
<set :tienda="'configuracion_tienda' | get('num != 0')[0]"></set>
<!-- Construir URLs dinámicas -->
<set :logo="tienda.logo.0.urlPath ? 'https://' ~ server.HTTP_HOST ~ tienda.logo.0.urlPath : 'https://' ~ server.HTTP_HOST ~ '/template/estandar/images/logo.png'"></set>
<!-- Twig set para expresiones complejas -->
{% set gracias = 'apartados' | get('num = 20').0 %}
Incluyendo módulos
Para incluir un módulo dentro de otro módulo o sección general, usa el ID del módulo como etiqueta HTML:
<module_id :param1="value1" :param2="value2"></module_id>
Ejemplo:
<header_menu :showLogo="true" :menuItems="items"></header_menu>
<product_card :product="selectedProduct" :showPrice="true"></product_card>
El módulo hijo recibe los parámetros como variables en su contexto.
Formularios (c-form)
Manejo automático de validación, almacenamiento en BD y envío de emails.
<c-form
class="max-w-2xl mx-auto p-6 bg-white rounded-lg shadow"
tableName="'solicitudes'"
mailRecord="['correos', 'CONTACTO']"
sendTo="'contacto@empresa.com'"
sendToClient="'email'"
captcha="true"
honeypot="true"
messageOK="'¡Gracias! Te contactaremos pronto'"
messageKO="'Por favor, completa todos los campos'"
redirect="'/gracias'"
attachFiles="true"
>
<div class="mb-4">
<label class="block mb-2">Nombre</label>
<input name="nombre" type="text" class="w-full p-2 border rounded" required>
</div>
<div class="mb-4">
<label class="block mb-2">Email</label>
<input name="email" type="text" class="w-full p-2 border rounded" required>
</div>
<div class="mb-4">
<label class="block mb-2">Mensaje</label>
<textarea name="mensaje" class="w-full p-2 border rounded" rows="5" required></textarea>
</div>
<div class="mb-4">
<label class="flex items-center">
<input name="acepto_politica" type="checkbox" class="mr-2" required>
<span>Acepto la política de privacidad</span>
</label>
</div>
<button type="submit" class="bg-teal-500 text-white px-6 py-2 rounded hover:bg-teal-600">Enviar</button>
<captcha/>
</c-form>
Atributos de c-form
| Atributo | Descripción |
|---|---|
tableName="'table'" |
Tabla donde almacenar registros |
mailRecord="['correos', 'ID']" |
Template de email de la tabla correos |
sendTo="'email@domain.com'" |
Destinatarios (separados por coma) |
sendToClient="'campo_email'" |
Campo con email del cliente para auto-reply |
captcha="true" |
Google reCAPTCHA |
honeypot="true" |
Campo oculto anti-spam |
messageOK="'texto'" |
Mensaje de éxito |
messageKO="'texto'" |
Mensaje de error |
redirect="'/path/'" |
Redirección tras envío exitoso |
attachFiles="true" |
Adjuntar archivos al email |
showImages="true" |
Mostrar thumbnails en email |
emailMode="'twig'" |
Email en formato Twig |
header="'<div>...</div>'" |
HTML cabecera del email |
footer="'<div>...</div>'" |
HTML footer del email |
styles="'body { ... }'" |
CSS para el email |
Componentes Built-in
Carousel (c-tns-wrapper)
<div class="c-tns-wrapper"
data-responsive='{"0":1,"768":2,"1024":3}'
data-speed="400"
data-nav="true"
data-autoplay-timeout="3000">
<div c-for="slide in record.slides">
<img src="{{ slide.image[0].urlPath }}" />
</div>
</div>
Lightbox
<a href="{{ image[0].urlPath }}" class="glightbox" data-gallery="gallery1">
<img src="{{ image[0].urlPath | imagec(400) }}" />
</a>
Breadcrumb
<breadCrumb/>
Animate On Scroll (AOS)
<div data-aos="fade-up" data-aos-delay="200">
Animated content
</div>
Lazy Loading
<img class="lazyload" data-src="{{ image[0].urlPath }}" />
<!-- o -->
<img data-lazy="true" src="{{ image[0].urlPath }}" />
Puntos importantes
- Nombres de variables:
data-field-label→ sin espacios ni caracteres especiales, minúsculas - Variables en multiv2: Son propiedades del objeto iterado (
record.nombre) - Campos upload: Retornan arrays, no strings (
imagen[0].urlPath, noimagen) - c-if usa
=no==:c-if="layout = 'grid'"(un solo igual) - c-for tabla: El nombre de tabla va sin prefijo
cms_ - Enlace: Ya incluye barras, no añadir extras
- Checkbox: Valores
1o0, notrue/false
MCP Tools: Config Vars e Imágenes de Módulos
Regla importante: Siempre rellenar variables al añadir un módulo
Cuando se añade un módulo a una página (con add_module_to_record), este queda vacío y no muestra nada visible. SIEMPRE hay que llamar a set_module_config_vars inmediatamente después para rellenar las variables con contenido de ejemplo coherente con el contexto del sitio. Incluir:
- Textos (títulos, descripciones, pretítulos) con contenido relevante al sitio
- Valores de listas/selects con una opción válida
- Para variables multi (records), crear al menos 2-3 items de ejemplo
- Para variables de imagen (upload), usar
generate_imageoupload_record_imagepara que el módulo se vea completo
Un módulo sin variables configuradas es invisible en la web.
Leer variables de un módulo
Antes de modificar cualquier módulo, usar get_module_config_vars para conocer el estado actual:
- tableName: tabla del registro padre (ej:
apartados), SIN prefijocms_ - recordNum: campo
numdel registro padre (ej:2) - sectionId: el
section_idde la instancia del módulo (ej:6c6d8)
Escribir variables de un módulo
Usar set_module_config_vars con los mismos tableName, recordNum y sectionId. Pasar todos los valores como strings.
La respuesta incluye configVars con el recordNum del registro builder_custom creado/actualizado y uploadFields para imágenes.
Tipos de almacenamiento (manejado automáticamente):
headfield,textfield,link,textbox,wysiwyg,upload→ se guardan en tablabuilder_customlist,checkbox,colorpicker→ se guardan directamente en el JSON config-vars (no en builder_custom)
No necesitas preocuparte por esto — set_module_config_vars lo maneja internamente. Solo pasa los valores como strings.
Subir imágenes a un módulo
El nombre del campo de imagen viene de builder.json → vars.NOMBRE.relations.builder_custom (ej: "image1"). NO es el nombre de la variable (ej: NO "imagenes").
Flujo correcto:
get_module_config_vars→ obtener elrecordNumen builder_custom de la variable de imagenupload_record_imagecon:tableName:"builder_custom"(siempre, sin prefijo cms_)recordId: elrecordNumdel paso 1 (ej:"778")fieldName: el campo de relations del builder.json (ej:"image1")imageUrl: URL completa accesible desde Docker
reorder_record_uploadssi es necesario — pasar array de upload IDs en el orden deseadolist_record_uploadspara verificar
Errores comunes a evitar:
- NO usar el sectionId como recordId — usar el
numde builder_custom - NO usar el nombre de la variable como fieldName — usar el campo de relations del builder.json (ej:
image1, noimagenes) - NO poner prefijo
cms_en tableName