Con los fundamentos de las anotaciones UI se logran rápidamente los primeros resultados. Sin embargo, para aplicaciones profesionales de Fiori Elements se necesitan técnicas avanzadas: áreas de página estructuradas con Facets, resaltados dinámicos, visibilidad condicional y visualizaciones analíticas.
Facets: Definir la Estructura de la Página
Los Facets determinan cómo está estructurada la Object Page. Definen áreas, pestañas y grupos de campos:
┌─────────────────────────────────────────────────────────────────────────┐│ [HeaderInfo: Número de reserva 12345 - Müller, Hans] │├─────────────────────────────────────────────────────────────────────────┤│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ││ │ General │ │ Posiciones │ │ Historial │ ← Pestañas Facet ││ └──────────────┘ └──────────────┘ └──────────────┘ │├─────────────────────────────────────────────────────────────────────────┤│ ┌───────────────────────────┐ ┌───────────────────────────┐ ││ │ Datos del Vuelo │ │ Pasajero │ ││ │ ───────────────────── │ │ ───────────────────── │ ││ │ Fecha: 15.03.2026 │ │ Nombre: Hans Müller │ ││ │ Salida: Frankfurt │ │ Email: [email protected] │ ││ │ Llegada: New York │ │ Teléfono: +49 123 456 │ ││ └───────────────────────────┘ └───────────────────────────┘ ││ ↑ FieldGroups │└─────────────────────────────────────────────────────────────────────────┘Construir la Jerarquía de Facets
define view entity ZC_FlightBooking as projection on ZI_FlightBooking{ @UI.facet: [ -- Área principal: Collection como contenedor { id: 'GeneralSection', purpose: #STANDARD, type: #COLLECTION, label: 'Información General', position: 10 }, -- FieldGroup dentro de la Collection { id: 'FlightData', parentId: 'GeneralSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'FlightData', label: 'Datos del Vuelo', position: 10 }, { id: 'PassengerData', parentId: 'GeneralSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'Passenger', label: 'Pasajero', position: 20 }, -- Pestaña propia para subposiciones { id: 'ItemsSection', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Posiciones', position: 20, targetElement: '_BookingItems' }, -- Pestaña para historial de cambios { id: 'HistorySection', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, targetQualifier: 'AdminData', label: 'Historial', position: 30 } ] key BookingId, ...}Definir FieldGroups
Los FieldGroups se referencian en los campos individuales:
@UI.fieldGroup: [{ qualifier: 'FlightData', position: 10 }] FlightDate,
@UI.fieldGroup: [{ qualifier: 'FlightData', position: 20 }] DepartureAirport,
@UI.fieldGroup: [{ qualifier: 'FlightData', position: 30 }] ArrivalAirport,
@UI.fieldGroup: [{ qualifier: 'Passenger', position: 10 }] PassengerName,
@UI.fieldGroup: [{ qualifier: 'Passenger', position: 20 }] @Semantics.eMail.address: true Email,
@UI.fieldGroup: [{ qualifier: 'Passenger', position: 30 }] @Semantics.telephone.type: [#WORK] Phone,
@UI.fieldGroup: [{ qualifier: 'AdminData', position: 10 }] CreatedBy,
@UI.fieldGroup: [{ qualifier: 'AdminData', position: 20 }] CreatedAt,
@UI.fieldGroup: [{ qualifier: 'AdminData', position: 30 }] LastChangedBy,
@UI.fieldGroup: [{ qualifier: 'AdminData', position: 40 }] LastChangedAtResumen de Tipos de Facet
| Tipo | Uso |
|---|---|
#COLLECTION | Contenedor para múltiples FieldGroups |
#FIELDGROUP_REFERENCE | Referencia campos de FieldGroup |
#LINEITEM_REFERENCE | Tabla con subposiciones |
#IDENTIFICATION_REFERENCE | Automático desde @UI.identification |
#DATAPOINT_REFERENCE | DataPoint individual (ej. KPI) |
#CHART_REFERENCE | Diagrama embebido |
HeaderInfo: El Encabezado de la Página
HeaderInfo define lo que se muestra en el header de la Object Page:
@UI.headerInfo: { typeName: 'Reserva de Vuelo', typeNamePlural: 'Reservas de Vuelo', title: { type: #STANDARD, value: 'BookingId', label: 'Número de Reserva' }, description: { type: #STANDARD, value: 'PassengerName', label: 'Pasajero' }, imageUrl: 'AirlineLogoUrl', typeImageUrl: 'sap-icon://flight'}define view entity ZC_FlightBookingPropiedades importantes de HeaderInfo:
| Propiedad | Descripción |
|---|---|
typeName | Denominación singular de la entidad |
typeNamePlural | Denominación plural para listas |
title | Título principal en el header |
description | Subtítulo en el header |
imageUrl | Imagen dinámica (campo con URL) |
typeImageUrl | Icono estático |
DataFieldForAction: Botones de Acción
Las acciones pueden colocarse en listas y en la Object Page:
@UI: { lineItem: [ -- Contenido de columna { position: 10, importance: #HIGH }, -- Acción en la cabecera de la lista { position: 100, type: #FOR_ACTION, dataAction: 'confirmBooking', label: 'Confirmar' }, { position: 110, type: #FOR_ACTION, dataAction: 'cancelBooking', label: 'Cancelar' } ], identification: [ { position: 10 }, -- Acción en la Object Page { position: 100, type: #FOR_ACTION, dataAction: 'confirmBooking', label: 'Confirmar Reserva' } ]}key BookingId,Acciones con Criticality
Las acciones pueden resaltarse con colores:
@UI.lineItem: [{ position: 110, type: #FOR_ACTION, dataAction: 'cancelBooking', label: 'Cancelar', criticality: #NEGATIVE -- Rojo}]| Criticality | Color | Uso |
|---|---|---|
#POSITIVE | Verde | Confirmar, Aprobar |
#NEGATIVE | Rojo | Eliminar, Cancelar |
#CRITICAL | Naranja | Advertencia, Pausa |
DataFieldForIntentBasedNavigation
Navegación a otras aplicaciones Fiori mediante Intent:
@UI.lineItem: [{ position: 50, type: #FOR_INTENT_BASED_NAVIGATION, semanticObject: 'Customer', action: 'display', label: 'Mostrar Cliente'}]CustomerId,
-- Con parámetros@UI.lineItem: [{ position: 60, type: #FOR_INTENT_BASED_NAVIGATION, semanticObject: 'Flight', action: 'manage', label: 'Gestionar Vuelo', mapping: [{ localElement: 'FlightId', semanticObjectAttribute: 'FlightId' }]}]FlightId,Requisito: La aplicación de destino debe estar configurada en SAP Fiori Launchpad con el Semantic Object y Action correspondiente.
Criticality y Highlighting
Criticality permite la codificación de colores dinámica basada en valores de datos:
Campo Criticality Calculado
define view entity ZI_FlightBooking{ BookingStatus,
-- Criticality como campo calculado case BookingStatus when 'N' then 2 -- Nuevo: Amarillo when 'C' then 3 -- Confirmado: Verde when 'X' then 1 -- Cancelado: Rojo else 0 -- Neutral: Gris end as StatusCriticality,
FlightPrice,
-- Criticality para precio case when FlightPrice > 2000 then 1 -- Rojo: Caro when FlightPrice > 1000 then 2 -- Amarillo: Medio else 3 -- Verde: Económico end as PriceCriticality,}Usar Criticality en Anotaciones UI
@UI.lineItem: [{ position: 30, importance: #HIGH, criticality: 'StatusCriticality', criticalityRepresentation: #WITH_ICON}]BookingStatus,
@UI.lineItem: [{ position: 40, criticality: 'PriceCriticality'}]@Semantics.amount.currencyCode: 'Currency'FlightPrice,
@UI.hidden: trueStatusCriticality,
@UI.hidden: truePriceCriticality,Valores de Criticality
| Valor | Significado | Color | Icono |
|---|---|---|---|
| 0 | Neutral | Gris | - |
| 1 | Negativo | Rojo | X |
| 2 | Crítico | Amarillo/Naranja | ! |
| 3 | Positivo | Verde | V |
| 5 | Nuevo Elemento | Azul | (especial) |
Visibilidad Condicional
Los campos pueden mostrarse u ocultarse basándose en valores de otros campos:
Hidden con Valor Dinámico
-- Campo visible solo para usuarios internos@UI.hidden: #( IsExternalUser )InternalNotes,
-- El campo de control (Boolean calculado)case when UserType = 'EXTERNAL' then abap_true else abap_falseend as IsExternalUser,Visibilidad Condicional mediante Anotaciones
-- Campo solo mostrar si Status = 'C' (confirmado)@UI.fieldGroup: [{ qualifier: 'ConfirmationData', position: 10, hidden: #( IsNotConfirmed )}]ConfirmationDate,
@UI.fieldGroup: [{ qualifier: 'ConfirmationData', position: 20, hidden: #( IsNotConfirmed )}]ConfirmedBy,
-- Campo de controlcase when BookingStatus <> 'C' then abap_true else abap_false end as IsNotConfirmed,Solo Lectura Basado en Estado
-- Campos solo editables si Status = 'N' (nuevo)@UI.fieldGroup: [{ qualifier: 'FlightData', position: 10}]@ObjectModel.readOnly: #( IsNotEditable )FlightDate,
case when BookingStatus <> 'N' then abap_true else abap_false end as IsNotEditable,Anotaciones de Chart
Diagramas embebidos en la Object Page:
Micro Chart en LineItem
@UI.lineItem: [{ position: 60, type: #AS_CHART, valueQualifier: 'OccupancyChart'}]@UI.chart: [{ qualifier: 'OccupancyChart', chartType: #BULLET, measures: ['OccupancyRate'], measureAttributes: [{ measure: 'OccupancyRate', role: #AXIS_1 }], description: 'Ocupación'}]@UI.dataPoint: { qualifier: 'OccupancyChart', targetValue: 100, criticalityCalculation: { improvementDirection: #MAXIMIZE, toleranceRangeLowValue: 50, deviationRangeLowValue: 25 }}OccupancyRate,Chart en Facet
@UI.facet: [{ id: 'AnalyticsSection', purpose: #STANDARD, type: #CHART_REFERENCE, targetQualifier: 'RevenueChart', label: 'Evolución de Ingresos', position: 40}]
@UI.chart: [{ qualifier: 'RevenueChart', chartType: #COLUMN, dimensions: ['Month'], measures: ['Revenue'], dimensionAttributes: [{ dimension: 'Month', role: #CATEGORY }], measureAttributes: [{ measure: 'Revenue', role: #AXIS_1 }]}]Tipos de Chart
| ChartType | Descripción |
|---|---|
#COLUMN | Diagrama de columnas |
#BAR | Diagrama de barras |
#LINE | Diagrama de líneas |
#PIE | Diagrama circular |
#DONUT | Diagrama de anillo |
#BULLET | Bullet Chart (Micro) |
#COMPARISON | Chart de comparación |
Anotaciones KPI
Indicadores Clave de Rendimiento en el header:
@UI.headerInfo: { typeName: 'Reserva de Vuelo', typeNamePlural: 'Reservas de Vuelo', title: { type: #STANDARD, value: 'BookingId' }}@UI.facet: [{ id: 'KPIHeader', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'TotalRevenue', position: 10}]define view entity ZC_FlightBooking{ @UI.dataPoint: { qualifier: 'TotalRevenue', title: 'Ingresos Totales', criticalityCalculation: { improvementDirection: #MAXIMIZE, toleranceRangeLowValue: 10000, deviationRangeLowValue: 5000 } } @Semantics.amount.currencyCode: 'Currency' TotalRevenue,}Múltiples KPIs en el Header
@UI.facet: [ { id: 'KPI_Revenue', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'Revenue', position: 10 }, { id: 'KPI_Bookings', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'BookingCount', position: 20 }, { id: 'KPI_Occupancy', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'Occupancy', position: 30 }]
@UI.dataPoint: { qualifier: 'Revenue', title: 'Ingresos', visualization: #NUMBER}TotalRevenue,
@UI.dataPoint: { qualifier: 'BookingCount', title: 'Reservas', visualization: #NUMBER}NumberOfBookings,
@UI.dataPoint: { qualifier: 'Occupancy', title: 'Ocupación', visualization: #PROGRESS, targetValue: 100}OccupancyPercent,Ejemplo Completo de Reserva de Vuelo
@EndUserText.label: 'Proyección de Reserva de Vuelo'@Metadata.allowExtensions: true
@UI.headerInfo: { typeName: 'Reserva de Vuelo', typeNamePlural: 'Reservas de Vuelo', title: { type: #STANDARD, value: 'BookingId' }, description: { type: #STANDARD, value: 'PassengerName' }, typeImageUrl: 'sap-icon://flight'}
define view entity ZC_FlightBooking as projection on ZI_FlightBooking{ @UI.facet: [ -- Header KPIs { id: 'KPI_Price', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'FlightPrice', position: 10 }, -- Área principal { id: 'GeneralSection', purpose: #STANDARD, type: #COLLECTION, label: 'Detalles de Reserva', position: 10 }, { id: 'FlightData', parentId: 'GeneralSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'Flight', label: 'Datos del Vuelo', position: 10 }, { id: 'PassengerData', parentId: 'GeneralSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'Passenger', label: 'Pasajero', position: 20 }, -- Área de administración { id: 'AdminSection', purpose: #STANDARD, type: #FIELDGROUP_REFERENCE, targetQualifier: 'Admin', label: 'Administración', position: 20 } ]
@UI: { lineItem: [ { position: 10, importance: #HIGH }, { position: 100, type: #FOR_ACTION, dataAction: 'confirmBooking', label: 'Confirmar' }, { position: 110, type: #FOR_ACTION, dataAction: 'cancelBooking', label: 'Cancelar', criticality: #NEGATIVE } ], identification: [ { position: 10 }, { position: 100, type: #FOR_ACTION, dataAction: 'confirmBooking', label: 'Confirmar Reserva' } ], selectionField: [{ position: 10 }] } key BookingId,
@UI: { lineItem: [{ position: 20, importance: #HIGH, criticality: 'StatusCriticality', criticalityRepresentation: #WITH_ICON }], identification: [{ position: 20 }], selectionField: [{ position: 20 }] } BookingStatus,
@UI.hidden: true StatusCriticality,
@UI: { lineItem: [{ position: 30, importance: #MEDIUM }], fieldGroup: [{ qualifier: 'Flight', position: 10 }] } FlightDate,
@UI.fieldGroup: [{ qualifier: 'Flight', position: 20 }] DepartureAirport,
@UI.fieldGroup: [{ qualifier: 'Flight', position: 30 }] ArrivalAirport,
@UI: { lineItem: [{ position: 40, importance: #HIGH }], dataPoint: { qualifier: 'FlightPrice', title: 'Precio' } } @Semantics.amount.currencyCode: 'Currency' FlightPrice,
@UI.hidden: true Currency,
@UI: { lineItem: [{ position: 50, type: #FOR_INTENT_BASED_NAVIGATION, semanticObject: 'Customer', action: 'display' }], fieldGroup: [{ qualifier: 'Passenger', position: 10 }] } PassengerName,
@UI.fieldGroup: [{ qualifier: 'Passenger', position: 20 }] @Semantics.eMail.address: true Email,
@UI.fieldGroup: [{ qualifier: 'Passenger', position: 30 }] @Semantics.telephone.type: [#WORK] Phone,
@UI.fieldGroup: [{ qualifier: 'Admin', position: 10, label: 'Creado por' }] @Semantics.user.createdBy: true CreatedBy,
@UI.fieldGroup: [{ qualifier: 'Admin', position: 20, label: 'Creado el' }] @Semantics.systemDateTime.createdAt: true CreatedAt,
@UI.fieldGroup: [{ qualifier: 'Admin', position: 30, label: 'Modificado por' }] @Semantics.user.lastChangedBy: true LastChangedBy,
@UI.fieldGroup: [{ qualifier: 'Admin', position: 40, label: 'Modificado el' }] @Semantics.systemDateTime.lastChangedAt: true LastChangedAt}Mejores Prácticas
| Aspecto | Recomendación |
|---|---|
| Estructura de Facets | Usar Collections para FieldGroups relacionados |
| Criticality | Usar con moderación, solo cuando tiene significado real (Estado, Límites) |
| Campos Hidden | Campos de control calculados siempre con @UI.hidden: true |
| HeaderInfo | Elegir título y descripción significativos |
| Acciones | Acciones importantes en lineItem, todas en identification |
| KPIs | Máximo 3-4 KPIs en el header para claridad |
| Charts | Micro Charts para tendencias, charts completos en Facets propios |
| Navegación | Intent-Based Navigation para navegación consistente entre apps |
Resumen
Las anotaciones UI avanzadas permiten aplicaciones profesionales de Fiori Elements sin código frontend. Con Facets se estructuran las páginas lógicamente, Criticality hace que los estados sean visualmente reconocibles, y Charts/KPIs proporcionan capacidades de análisis directamente en la aplicación.
Artículos relacionados: Fundamentos de CDS Annotations para los básicos, RAP Actions y Functions para implementación de acciones, Mensajes RAP en Fiori para feedback al usuario y UI Fiori Elements sin Código para comenzar.