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, íconos, insignias, enlaces, botones de acción o cualquier otro elemento visual que haga que los datos sean más fáciles de leer y 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 sin importar la cantidad de filas.
Asigna un renderizador a una columna usando setRenderer(). El renderizador se aplica de manera uniforme a cada celda de 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, utiliza un proveedor de valores en su lugar. Los renderizadores crean elementos DOM adicionales para cada fila renderizada, lo que implica un costo en el tiempo de renderizado. Reserva los renderizadores para salidas visuales como íconos, insignias, botones o cualquier presentación basada en HTML.
webforJ incluye 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 repasan cuatro renderizadores utilizados con frecuencia y demuestran el patrón setRenderer() en la práctica.
TextRenderer
Muestra el contenido de la celda 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 sea 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, coloreado automático (colores distintos automáticos por valor único) y un ícono opcional al principio. Úsalo para valores categóricos como etiquetas, tipos o etiquetas 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 íconos. Úsalo para cualquier columna de verdadero/falso donde un ícono comunique el valor más rápidamente que el texto, como indicadores de características, estados activos/inactivos o campos de opt-in.
// Íconos predeterminados
BooleanRenderer<Task> renderer = new BooleanRenderer<>();
table.addColumn("completed", Task::isCompleted).setRenderer(renderer);
// Íconos 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 una cantidad monetaria utilizando las reglas del Locale suministrado. Úsalo para cualquier columna monetaria donde el formateo correcto del 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 fondo de captura general con otherwise().
El siguiente ejemplo muestra el renderizado condicional aplicado a una columna de estado de factura, alternando entre variantes de BadgeRenderer según el valor:
Mostrar Código
- InvoiceListView.java
- Invoice.java
- InvoiceService.java
También funciona bien para umbrales numéricos. Este panel del servidor utiliza ConditionalRenderer para cambiar los temas de ProgressBarRenderer según los niveles de uso de CPU y memoria:
Mostrar Código
- ServerDashboardView.java
- Server.java
- ServerService.java
API de condición
Las condiciones se construyen con métodos de fábrica estáticos y pueden combinarse 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)
// Booleano / vacío
Condition.isTrue()
Condition.isFalse()
Condition.isEmpty()
// Coincidencia de cadena
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 de JavaScript cruda
Condition.expression("cell.value % 2 === 0")
Renderizado compuesto
CompositeRenderer combina múltiples renderizadores uno al lado del otro en una única celda utilizando un diseño flexible. Úsalo para emparejar un ícono 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 utiliza un CompositeRenderer en la columna Employee para mostrar un avatar autogenerado junto al nombre de cada empleado:
Mostrar Código
- EmployeeDirectoryView.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 más información sobre cómo se utiliza la sintaxis de Lodash para acceder a la información de la celda y crear renderizadores informativos, consulta esta sección de referencia.
Accediendo a múltiples columnas
Utiliza cell.row.getValue("columnId") para leer columnas hermanas dentro de la plantilla. Esto es útil para combinar campos, calcular diferencias o hacer referencia a 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 forma 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 se establece setLazyRender(true) 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
- LazyRenderView.java
Los renderizadores de celda crean más entidades dentro del DOM, lo que significa más trabajo de CPU durante el renderizado, sin importar qué renderizador lo cree.
El renderizado perezoso puede ayudar a reducir el impacto en el rendimiento si un renderizador es realmente necesario. Si solo necesitas cambiar o formatear el valor, y no estás creando un DOM complejo, utiliza un proveedor de valores en su lugar para transformar el valor.
Referencia de renderizadores integrados
webforJ incluye un conjunto completo de renderizadores para los casos de uso más comunes. Asigna cualquiera de ellos a una columna utilizando column.setRenderer(renderer).
Mostrar Código
- ProductCatalogView.java
- Product.java
- ProductService.java
Texto y 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, coloreado automático (colores distintos automáticos por valor único) y un ícono opcional al principio.
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
Rinde una cadena de respaldo configurable cuando el valor de la celda es null o está vacío; de lo contrario, rinde el valor tal cual.
table.addColumn("notes", MusicRecord::getNotes)
.setRenderer(new NullRenderer<>("N/A"));
Estado e indicadores
BooleanRenderer - verdadero/falso/nulo mostrado como íconos
Reemplaza los valores true, false y null con íconos. Por defecto, utiliza un signo de verificación, una cruz y un guion.
// Íconos predeterminados
BooleanRenderer renderer = new BooleanRenderer<>();
table.addColumn("completed", Task::isCompleted).setRenderer(renderer);
// Íconos personalizados
BooleanRenderer custom = new BooleanRenderer<>(
TablerIcon.create("thumb-up").setTheme(Theme.SUCCESS),
TablerIcon.create("thumb-down").setTheme(Theme.DANGER)
);
StatusDotRenderer - punto indicador coloreado al lado del texto de la celda
Rinde un pequeño punto coloreado a la izquierda del valor de la celda. Asocia 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 consciente del locale
Formatea un valor numérico como una cantidad monetaria utilizando las reglas del Locale suministrado.
// 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. Establece el segundo argumento del constructor en false para evitar renderizar una barra de progreso delgada 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
Rinde una barra de progreso de ancho completo con límites mínimos y máximos configurables, modo indeterminado y visualización a rayas 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 caracteres a un valor de cadena. # coincide con cualquier dígito; los caracteres literales se conservan. Consulta reglas de máscara de texto para conocer todos los caracteres de máscara soportados.
table.addColumn("ssn", Employee::getSsn)
.setRenderer(new MaskedTextRenderer<>("###-##-####"));
MaskedNumberRenderer - valor numérico formateado con una máscara de número
Formatea un valor numérico utilizando una cadena de patrón con separadores conscientes del locale. 0 fuerza un dígito; # es opcional. Consulta reglas de máscara de número para conocer 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 utilizando tokens de patrón: %Mz (mes), %Dz (día), %Yz (año), y otros. Consulta reglas de máscara de fecha para conocer 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 clickable
Envuelve el valor de la celda en un ancla mailto:. Un ícono de correo de tema primario sirve como la pista visual por defecto.
// Ícono de correo predeterminado
table.addColumn("email", Contact::getEmail)
.setRenderer(new EmailRenderer<>());
// Ícono personalizado
table.addColumn("email", Contact::getEmail)
.setRenderer(new EmailRenderer<>(TablerIcon.create("at")));
PhoneRenderer - número de teléfono como un enlace tel clickable
Envuelve el valor de la celda en un ancla tel:. En dispositivos móviles, al tocar se abre el marcador. Un ícono de teléfono de tema primario se muestra por defecto.
// Ícono de teléfono predeterminado
table.addColumn("phone", Contact::getPhone)
.setRenderer(new PhoneRenderer<>());
// Ícono personalizado
table.addColumn("phone", Contact::getPhone)
.setRenderer(new PhoneRenderer<>(TablerIcon.create("device-mobile")));
AnchorRenderer - valor de celda como un hipervínculo configurable
Rinde un elemento ancla clickable. 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
Rinde un componente de avatar. Las iniciales se derivan automáticamente del valor de la celda. Soporta temas y un ícono de respaldo.
AvatarRenderer renderer = new AvatarRenderer<>();
renderer.setTheme(AvatarTheme.PRIMARY);
renderer.setIcon(TablerIcon.create("user"));
table.addColumn("artist", MusicRecord::getArtist).setRenderer(renderer);
Íconos y acciones
IconRenderer - ícono independiente, opcionalmente clickable
Rinde un solo ícono. Adjunta un listener de clic para un comportamiento interactivo.
IconRenderer renderer = new IconRenderer<>(TablerIcon.create("music"));
table.addColumn("type", MusicRecord::getMusicType).setRenderer(renderer);
IconButtonRenderer - botón de ícono accionable con acceso a la fila
Rinde un botón de ícono clickable. 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
Rinde 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
Rinde cualquier elemento HTML con una cadena de contenido de plantilla de lodash. Este es el enfoque de escape para situaciones donde 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 muestra la información dentro de una Tabla. La clase principal, Renderer, está diseñada para ser extendida para crear renderizadores personalizados basados en plantillas de lodash, lo que permite un renderizado dinámico e interactivo de contenido.
Las plantillas de lodash permiten la inserción de HTML directamente en las celdas de la tabla, haciéndolas muy efectivas para renderizar datos de celdas complejos en una Tabla. Este enfoque permite la generación dinámica de HTML basado en los datos de la celda, facilitando contenido rico e interactivo en las celdas de la tabla.
Sintaxis de Lodash
La siguiente sección describe los conceptos básicos de la sintaxis de Lodash. Si bien no es una descripción exhaustiva o completa, se puede usar para comenzar a utilizar Lodash dentro del componente Tabla.
Resumen de sintaxis para plantillas de lodash:
<%= ... %>- Interpole valores, insertando el resultado del código JavaScript en la plantilla.<% ... %>- Ejecuta código JavaScript, permitiendo bucles, condicionales y más.<%- ... %>- Escapa el contenido HTML, asegurando que los datos interpolados sean seguros contra ataques de inyección HTML.
Ejemplos utilizando datos de celdas:
1. Interpolación de valor simple: muestra directamente el valor de la celda.
<%= cell.value %>
2. Renderizado condicional: utiliza lógica de JavaScript para renderizar condicionalmente contenido.
<% if (cell.value > 100) { %> 'Alto' <% } else { %> 'Normal' <% } %>
3. Combinando 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: rinde de forma segura el contenido generado por el usuario.
El renderizador tiene acceso a propiedades detalladas de la 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 única 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 del que obtener los datos de la celda. |
| label | String | El nombre que se renderiza en el encabezado de la columna. |
| pinned | ColumnPinDirection | La dirección de fijación de la columna (izquierda, derecha, automática). |
| 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. |