Rendering
Un renderizador controla cómo se muestra cada celda en una columna. En lugar de mostrar un valor en bruto, un renderizador transforma los datos de cada celda en texto estilizado, iconos, insignias, enlaces, botones de acción u otro tipo de visual que hace que los datos sean más rápidos de leer y más fáciles de actuar.
El renderizado ocurre completamente en el navegador. El servidor envía datos en bruto y el cliente maneja la presentación, haciendo que la 'Tabla' sea rápida independientemente del número de filas.
Asigna un renderizador a una columna usando setRenderer(). El renderizador se aplica de manera uniforme a cada celda en esa columna:
TextRenderer<MusicRecord> renderer = new TextRenderer<>();
renderer.setTheme(Theme.PRIMARY);
table.addColumn("title", MusicRecord::getTitle).setRenderer(renderer);
Si solo necesitas transformar o formatear un valor de celda sin producir ninguna estructura DOM, usa un proveedor de valores en su lugar. Los renderizadores crean elementos DOM adicionales para cada fila renderizada, lo que conlleva un costo en el momento del renderizado. Reserva los renderizadores para salidas visuales como iconos, insignias, botones o cualquier presentación basada en HTML.
webforJ se envía con renderizadores integrados para los casos de uso más comunes. Para cualquier cosa específica de tu aplicación, extiende Renderer e implementa build() para devolver una cadena de plantilla de lodash que se ejecute en el navegador para cada celda.
Renderizadores comunes
Los siguientes ejemplos explican cuatro renderizadores de uso frecuente y demuestran el patrón setRenderer() en la práctica.
TextRenderer
Muestra el contenido de las celdas como texto plano o estilizado. Aplica un color de tema o decoración de texto a una columna sin cambiar su estructura, como resaltar un campo de prioridad en rojo o hacer que un identificador clave esté en negrita.
TextRenderer<MusicRecord> renderer = new TextRenderer<>();
renderer.setTheme(Theme.PRIMARY);
renderer.setDecorations(EnumSet.of(TextDecoration.BOLD));
table.addColumn("title", MusicRecord::getTitle).setRenderer(renderer);
BadgeRenderer
Envuelve el valor de la celda en un elemento de insignia. Soporta temas, expansiones, siembra de colores (colores distintos automáticos por valor único) y un icono principal opcional. Úsalo para valores categóricos como etiquetas, tipos o descripciones donde chips visuales distintos ayudan a los usuarios a escanear y comparar filas rápidamente.
BadgeRenderer<MusicRecord> renderer = new BadgeRenderer<>();
renderer.setTheme(BadgeTheme.PRIMARY);
table.addColumn("musicType", MusicRecord::getMusicType).setRenderer(renderer);
BooleanRenderer
Reemplaza los valores true, false y null con iconos. Úsalo para cualquier columna de verdadero/falso donde un icono comunique el valor más rápido que el texto, como indicadores de función, estados activo/inactivo o campos de opt-in.
// Iconos predeterminados
BooleanRenderer<Task> renderer = new BooleanRenderer<>();
table.addColumn("completed", Task::isCompleted).setRenderer(renderer);
// Iconos personalizados
BooleanRenderer<Task> custom = new BooleanRenderer<>(
TablerIcon.create("thumb-up").setTheme(Theme.SUCCESS),
TablerIcon.create("thumb-down").setTheme(Theme.DANGER)
);
table.addColumn("completed", Task::isCompleted).setRenderer(custom);
CurrencyRenderer
Formatea un valor numérico como un monto en moneda usando las reglas del Locale proporcionado. Úsalo para cualquier columna monetaria donde el formateo correcto por locale (símbolo, separadores, lugares decimales) sea importante.
// Dólares estadounidenses
table.addColumn("cost", MusicRecord::getCost)
.setRenderer(new CurrencyRenderer<>(Locale.US));
// Euros con locale alemán
table.addColumn("retail", MusicRecord::getRetail)
.setRenderer(new CurrencyRenderer<>(Locale.GERMANY));
Renderizado condicional
ConditionalRenderer selecciona un renderizador diferente por celda basado en el valor de la celda. Las condiciones se evalúan en orden; la primera coincidencia gana. Se puede establecer un retorno general con otherwise().
El siguiente ejemplo muestra el renderizado condicional aplicado a una columna de estado de factura, cambiando entre variantes de BadgeRenderer según el valor:
Mostrar Código
- Java
También funciona bien para umbrales numéricos. Este panel de control del servidor usa ConditionalRenderer para cambiar los temas de ProgressBarRenderer según los niveles de uso de CPU y memoria:
Mostrar Código
- Java
API de condiciones
Las condiciones se construyen con métodos de fábrica estática y se pueden componer con and(), or(), y negate().
// Igualdad de valor
Condition.equalTo("active")
Condition.equalToIgnoreCase("active")
Condition.in("active", "pending", "new")
// Comparaciones numéricas
Condition.greaterThan(100)
Condition.lessThanOrEqual(0)
Condition.between(10, 50)
// Booleanos / vacuidad
Condition.isTrue()
Condition.isFalse()
Condition.isEmpty()
// Coincidencia de cadenas
Condition.contains("error")
Condition.containsIgnoreCase("warn")
// Composición
Condition.greaterThan(0).and(Condition.lessThan(100))
Condition.isEmpty().or(Condition.equalTo("N/A"))
Condition.isTrue().negate()
// Verificación entre columnas
Condition.column("status").equalTo("active")
// Expresión JavaScript en bruto
Condition.expression("cell.value % 2 === 0")
Renderizado compuesto
CompositeRenderer combina múltiples renderizadores uno al lado del otro en una sola celda usando un diseño flex. Úsalo para emparejar un icono con texto, mostrar un avatar junto a un nombre o apilar una insignia junto a un indicador de estado.
El directorio de empleados a continuación usa un CompositeRenderer en la columna Empleado para mostrar un avatar autogenerado junto al nombre de cada empleado:
Mostrar Código
- Java
Renderizadores personalizados
Cuando ningún renderizador integrado se adapta a tu caso de uso, extiende Renderer e implementa build(). El método devuelve una cadena de plantilla de lodash que se ejecuta en el navegador para cada celda en la columna, expresada como una mezcla de HTML y JavaScript.
Creando un renderizador personalizado
Paso 1: Extiende Renderer con tu tipo de dato de fila.
public class RatingRenderer extends Renderer<MusicRecord> {
Paso 2: Sobrescribe build() y devuelve una cadena de plantilla de lodash.
@Override
public String build() {
return /* html */"""
<%
const rating = Number(cell.value);
const stars = Math.round(Math.min(Math.max(rating, 0), 5));
const full = '★'.repeat(stars);
const empty = '☆'.repeat(5 - stars);
%>
<span><%= full %><%= empty %></span>
<span style="color: var(--dwc-color-body-text)">(<%= rating.toFixed(1) %>)</span>
""";
}
}
Paso 3: Asigna el renderizador a una columna.
table.addColumn("rating", MusicRecord::getRating)
.setRenderer(new RatingRenderer());
Para obtener más información sobre cómo la sintaxis de Lodash se usa para acceder a la información de la celda y crear renderizadores informativos, consulta esta sección de referencia.
Accediendo a múltiples columnas
Usa cell.row.getValue("columnId") para leer columnas hermanas dentro de la plantilla. Esto es útil para combinar campos, calcular deltas o referenciar datos relacionados.
public class ArtistAvatarRenderer extends Renderer<MusicRecord> {
@Override
public String build() {
return /* html */"""
<%
const name = cell.row.getValue("artist");
const initials = name
? name.split(' ').map(w => w.charAt(0)).join('').substring(0, 2).toUpperCase()
: '?';
%>
<div style="display: flex; align-items: center; gap: 8px;">
<div style="width: 28px; height: 28px; border-radius: 50%;
background: var(--dwc-color-primary); color: white;
display: flex; align-items: center; justify-content: center;
font-size: 11px; font-weight: 600;">
<%= initials %>
</div>
<span><%= name %></span>
</div>
""";
}
}
Eventos de clic
IconButtonRenderer y ButtonRenderer exponen addClickListener() de manera predeterminada. El evento de clic proporciona acceso al objeto de datos de la fila a través de e.getItem().
IconButtonRenderer<MusicRecord> deleteBtn = new IconButtonRenderer<>(
TablerIcon.create("trash").setTheme(Theme.DANGER)
);
deleteBtn.addClickListener(e -> {
MusicRecord record = e.getItem();
repository.delete(record);
table.refresh();
});
table.addColumn("delete", r -> "").setRenderer(deleteBtn);
Rendimiento: renderizado perezoso 25.12
Para columnas que utilizan renderizadores visualmente costosos como insignias, barras de progreso, avatares o componentes web, habilita el renderizado perezoso para mejorar el rendimiento de desplazamiento.
table.addColumn("status", Order::getStatus)
.setRenderer(new BadgeRenderer<>())
.setLazyRender(true);
Cuando setLazyRender(true) está configurado en una columna, las celdas muestran un marcador de posición animado ligero mientras el usuario está desplazándose. El contenido real de la celda se renderiza una vez que se detiene el desplazamiento. Esta es una configuración a nivel de columna, por lo que puedes habilitarla selectivamente solo para las columnas que se benefician.
Mostrar Código
- Java
Los renderizadores de celdas crean más entidades dentro del DOM, lo que significa más trabajo de CPU durante el renderizado, sin importar qué renderizador lo crea.
El renderizado perezoso puede ayudar a reducir el impacto en el rendimiento si realmente se necesita un renderizador. Si solo necesitas cambiar o formatear el valor, y no estás creando un DOM complejo, usa un proveedor de valores en su lugar para transformar el valor.
Referencia de renderizadores integrados
webforJ se envía con un conjunto completo de renderizadores para los casos de uso más comunes. Asigna cualquiera de ellos a una columna usando column.setRenderer(renderer).
Mostrar Código
- Java
Texto e etiquetas
TextRenderer - texto estilizado con tema y decoraciones opcionales
Muestra el contenido de la celda como texto plano o estilizado. Soporta colores de tema y decoraciones de texto como negrita, cursiva y subrayado.
TextRenderer renderer = new TextRenderer<>();
renderer.setTheme(Theme.PRIMARY);
renderer.setDecorations(EnumSet.of(TextDecoration.BOLD));
table.addColumn("title", MusicRecord::getTitle).setRenderer(renderer);
BadgeRenderer - valor mostrado dentro de una insignia
Envuelve el valor de la celda en un elemento de insignia. Soporta temas, expansiones, siembra de colores (colores distintos automáticos por valor único) y un icono principal opcional.
BadgeRenderer renderer = new BadgeRenderer<>();
renderer.setTheme(BadgeTheme.PRIMARY);
table.addColumn("musicType", MusicRecord::getMusicType).setRenderer(renderer);
NullRenderer - marcador de posición para valores nulos o vacíos
Renderiza una cadena de retorno configurable cuando el valor de la celda es null o está vacío; de lo contrario, renderiza el valor tal como está.
table.addColumn("notes", MusicRecord::getNotes)
.setRenderer(new NullRenderer<>("N/A"));
Estado e indicadores
BooleanRenderer - true/false/null mostrado como iconos
Reemplaza los valores true, false y null con iconos. Por defecto, usa una marca de verificación, una cruz y un guion.
// Iconos predeterminados
BooleanRenderer renderer = new BooleanRenderer<>();
table.addColumn("completed", Task::isCompleted).setRenderer(renderer);
// Iconos personalizados
BooleanRenderer custom = new BooleanRenderer<>(
TablerIcon.create("thumb-up").setTheme(Theme.SUCCESS),
TablerIcon.create("thumb-down").setTheme(Theme.DANGER)
);
StatusDotRenderer - punto indicador de color al lado del texto de la celda
Renderiza un pequeño punto de color a la izquierda del valor de la celda. Mapea valores individuales a temas, cadenas de color CSS o instancias de java.awt.Color.
StatusDotRenderer renderer = new StatusDotRenderer<>();
renderer.addMapping("Active", Theme.SUCCESS);
renderer.addMapping("Pending", Theme.WARNING);
renderer.addMapping("Cancelled", Theme.DANGER);
table.addColumn("status", Order::getStatus).setRenderer(renderer);
Números, moneda y fechas
CurrencyRenderer - formateo de moneda con conciencia sobre la localidad
Formatea un valor numérico como un monto en moneda usando las reglas del Locale proporcionado.
// Dólares estadounidenses
table.addColumn("cost", MusicRecord::getCost)
.setRenderer(new CurrencyRenderer<>(Locale.US));
// Euros con locale alemán
table.addColumn("retail", MusicRecord::getRetail)
.setRenderer(new CurrencyRenderer<>(Locale.GERMANY));
PercentageRenderer - porcentaje con barra de progreso opcional
Muestra un valor numérico como un porcentaje. Configura el segundo argumento del constructor como false para evitar renderizar una delgada barra de progreso debajo del texto.
PercentageRenderer renderer = new PercentageRenderer<>(Theme.PRIMARY, true);
table.addColumn("completion", Task::getCompletion).setRenderer(renderer);
ProgressBarRenderer - barra de progreso completa para valores numéricos
Renderiza una barra de progreso de ancho completo con límites mínimos y máximos configurables, modo indeterminado, y visualización rayada o animada. Usa setText() con una expresión de lodash para superponer texto personalizado en la barra.
ProgressBarRenderer renderer = new ProgressBarRenderer<>();
renderer.setMax(100);
renderer.setTheme(Theme.SUCCESS);
renderer.setTextVisible(true);
renderer.setText("<%= cell.value %>/100");
table.addColumn("progress", Task::getProgress).setRenderer(renderer);
MaskedTextRenderer - cadena formateada con una máscara de texto
Aplica una máscara de carácter a un valor de cadena. # coincide con cualquier dígito; los caracteres literales se conservan. Consulta reglas de máscara de texto para ver todos los caracteres de máscara soportados.
table.addColumn("ssn", Employee::getSsn)
.setRenderer(new MaskedTextRenderer<>("###-##-####"));
MaskedNumberRenderer - valor numérico formateado con una máscara numérica
Formatea un valor numérico usando una cadena de patrón con separadores conscientes de la localidad. 0 obliga a un dígito; # es opcional. Consulta reglas de máscara de número para ver todos los caracteres de máscara soportados.
table.addColumn("price", Product::getPrice)
.setRenderer(new MaskedNumberRenderer<>("###,##0.00", Locale.US));
MaskedDateTimeRenderer - valor de fecha/hora con una máscara de fecha
Formatea un valor de fecha u hora usando tokens de patrón: %Mz (mes), %Dz (día), %Yz (año) y otros. Consulta reglas de máscara de fecha para ver todos los tokens disponibles.
table.addColumn("released", MusicRecord::getReleaseDate)
.setRenderer(new MaskedDateTimeRenderer<>("%Mz/%Dz/%Yz"));
Enlaces y medios
EmailRenderer - dirección de correo electrónico como un enlace mailto clickeable
Envuelve el valor de la celda en un ancla de mailto:. Un icono de correo temático principal sirve como la señal visual de forma predeterminada.
// Icono de correo predeterminado
table.addColumn("email", Contact::getEmail)
.setRenderer(new EmailRenderer<>());
// Icono personalizado
table.addColumn("email", Contact::getEmail)
.setRenderer(new EmailRenderer<>(TablerIcon.create("at")));
PhoneRenderer - número de teléfono como un enlace tel clickeable
Envuelve el valor de la celda en un ancla de tel:. En dispositivos móviles, tocarlo abre el marcador. Un icono de teléfono temático principal se muestra por defecto.
// Icono de teléfono predeterminado
table.addColumn("phone", Contact::getPhone)
.setRenderer(new PhoneRenderer<>());
// Icono personalizado
table.addColumn("phone", Contact::getPhone)
.setRenderer(new PhoneRenderer<>(TablerIcon.create("device-mobile")));
AnchorRenderer - valor de celda como un enlace hipertexto configurable
Renderiza un elemento ancla clickeable. El href soporta expresiones de plantilla de lodash para que puedas construir URLs dinámicamente a partir del valor de la celda.
AnchorRenderer renderer = new AnchorRenderer<>();
renderer.setHref("https://www.google.com/search?q=<%= cell.value %>");
renderer.setTarget("_blank");
table.addColumn("title", MusicRecord::getTitle).setRenderer(renderer);
ImageRenderer - imagen en línea en una celda
Muestra una imagen. El atributo src soporta expresiones de plantilla de lodash para que cada fila pueda mostrar una imagen diferente.
ImageRenderer renderer = new ImageRenderer<>();
renderer.setSrc("https://placehold.co/40x40?text=<%= cell.value %>");
renderer.setAlt("Cover");
table.addColumn("cover", MusicRecord::getArtist).setRenderer(renderer);
Personas y avatares
AvatarRenderer - avatar con iniciales autogeneradas
Renderiza un componente de avatar. Las iniciales se derivan automáticamente del valor de la celda. Soporta temas y un icono de respaldo.
AvatarRenderer renderer = new AvatarRenderer<>();
renderer.setTheme(AvatarTheme.PRIMARY);
renderer.setIcon(TablerIcon.create("user"));
table.addColumn("artist", MusicRecord::getArtist).setRenderer(renderer);
Iconos y acciones
IconRenderer - icono independiente, opcionalmente clickeable
Renderiza un solo icono. Adjunta un listener de clic para comportamiento interactivo.
IconRenderer renderer = new IconRenderer<>(TablerIcon.create("music"));
table.addColumn("type", MusicRecord::getMusicType).setRenderer(renderer);
IconButtonRenderer - botón de icono clickeable con acceso a la fila
Renderiza un botón de icono clickeable. El evento de clic expone el elemento de la fila a través de e.getItem(), lo que lo hace ideal para acciones a nivel de fila.
IconButtonRenderer renderer = new IconButtonRenderer<>(TablerIcon.create("edit"));
renderer.addClickListener(e -> openEditor(e.getItem()));
table.addColumn("actions", r -> "").setRenderer(renderer);
ButtonRenderer - botón temático en una celda
Renderiza un componente Button completo dentro de la celda.
ButtonRenderer renderer = new ButtonRenderer<>("Edit");
renderer.setTheme(ButtonTheme.PRIMARY);
renderer.addClickListener(e -> openEditor(e.getItem()));
table.addColumn("edit", r -> "Edit").setRenderer(renderer);
ElementRenderer - elemento HTML en bruto con contenido de lodash
Renderiza cualquier elemento HTML con una cadena de contenido de plantilla de lodash. Este es un salvavidas para situaciones en las que ningún renderizador integrado se adapta.
ElementRenderer renderer = new ElementRenderer<>("span", "<%= cell.value %>");
table.addColumn("custom", MusicRecord::getTitle).setRenderer(renderer);
Referencia de plantilla
Los renderizadores ofrecen un mecanismo poderoso para personalizar la forma en que se muestran los datos dentro de una Tabla. La clase principal, Renderer, está diseñada para extenderse a crear renderizadores personalizados basados en plantillas de lodash, lo que permite la renderización de contenido dinámico e interactivo.
Las plantillas de lodash permiten insertar HTML directamente en las celdas de la tabla, haciéndolas muy efectivas para renderizar datos de celdas complejas en una Tabla. Este enfoque permite la generación dinámica de HTML basada en los datos de la celda, facilitando contenido de celda rico e interactivo.
Sintaxis de Lodash
La siguiente sección describe los conceptos básicos de la sintaxis de Lodash. Aunque esta no es una visión completa o exhaustiva, se puede utilizar para ayudar a comenzar a usar Lodash dentro del componente Tabla.
Resumen de sintaxis para plantillas de lodash:
<%= ... %>- Interpola valores, insertando el resultado del código JavaScript en la plantilla.<% ... %>- Ejecuta código JavaScript, permitiendo bucles, condicionales y más.<%- ... %>- Escapa contenido HTML, asegurando que los datos interpolados sean seguros contra ataques de inyección HTML.
Ejemplos usando datos de celdas:
1. Interpolación de valor simple: muestra directamente el valor de la celda.
<%= cell.value %>
2. Renderizado condicional: usa la lógica de JavaScript para renderizar contenido condicionalmente.
<% if (cell.value > 100) { %> 'High' <% } else { %> 'Normal' <% } %>
3. Combinación de campos de datos: renderiza contenido usando múltiples campos de datos de la celda.
<%= cell.row.getValue('firstName') + ' ' + cell.row.getValue('lastName') %>
4. Escapando contenido HTML: renderiza de forma segura contenido generado por el usuario.
El renderizador tiene acceso a propiedades detalladas de celda, fila y columna en el lado del cliente:
Propiedades de TableCell:
| Propiedad | Tipo | Descripción |
|---|---|---|
| column | TableColumn | El objeto de columna asociado. |
| first | boolean | Indica si la celda es la primera en la fila. |
| id | String | La ID de la celda. |
| index | int | El índice de la celda dentro de su fila. |
| last | boolean | Indica si la celda es la última en la fila. |
| row | TableRow | El objeto de fila asociado para la celda. |
| value | Object | El valor en bruto de la celda, directamente de la fuente de datos. |
Propiedades de TableRow:
| Propiedad | Tipo | Descripción |
|---|---|---|
| cells | TableCell[] | Las celdas dentro de la fila. |
| data | Object | Los datos proporcionados por la aplicación para la fila. |
| even | boolean | Indica si la fila es de número par (para propósitos de estilo). |
| first | boolean | Indica si la fila es la primera en la tabla. |
| id | String | ID único para la fila. |
| index | int | El índice de la fila. |
| last | boolean | Indica si la fila es la última en la tabla. |
| odd | boolean | Indica si la fila es de número impar (para propósitos de estilo). |
Propiedades de TableColumn:
| Propiedad | Tipo | Descripción |
|---|---|---|
| align | ColumnAlignment | La alineación de la columna (izquierda, centro, derecha). |
| id | String | El campo del objeto de fila para obtener los datos de la celda. |
| label | String | El nombre a renderizar en el encabezado de la columna. |
| pinned | ColumnPinDirection | La dirección de anclaje de la columna (izquierda, derecha, automático). |
| sortable | boolean | Si es verdadero, la columna se puede ordenar. |
| sort | SortDirection | El orden de clasificación de la columna. |
| type | ColumnType | El tipo de la columna (texto, número, booleano, etc.). |
| minWidth | number | El ancho mínimo de la columna en píxeles. |