Anotaciones UI para RAP: Técnicas Avanzadas para Fiori Elements

Kategorie
RAP
Veröffentlicht
Autor
Johannes

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 }]
LastChangedAt

Resumen de Tipos de Facet

TipoUso
#COLLECTIONContenedor para múltiples FieldGroups
#FIELDGROUP_REFERENCEReferencia campos de FieldGroup
#LINEITEM_REFERENCETabla con subposiciones
#IDENTIFICATION_REFERENCEAutomático desde @UI.identification
#DATAPOINT_REFERENCEDataPoint individual (ej. KPI)
#CHART_REFERENCEDiagrama 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_FlightBooking

Propiedades importantes de HeaderInfo:

PropiedadDescripción
typeNameDenominación singular de la entidad
typeNamePluralDenominación plural para listas
titleTítulo principal en el header
descriptionSubtítulo en el header
imageUrlImagen dinámica (campo con URL)
typeImageUrlIcono 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
}]
CriticalityColorUso
#POSITIVEVerdeConfirmar, Aprobar
#NEGATIVERojoEliminar, Cancelar
#CRITICALNaranjaAdvertencia, 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: true
StatusCriticality,
@UI.hidden: true
PriceCriticality,

Valores de Criticality

ValorSignificadoColorIcono
0NeutralGris-
1NegativoRojoX
2CríticoAmarillo/Naranja!
3PositivoVerdeV
5Nuevo ElementoAzul(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_false
end 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 control
case 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

ChartTypeDescripción
#COLUMNDiagrama de columnas
#BARDiagrama de barras
#LINEDiagrama de líneas
#PIEDiagrama circular
#DONUTDiagrama de anillo
#BULLETBullet Chart (Micro)
#COMPARISONChart 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

AspectoRecomendación
Estructura de FacetsUsar Collections para FieldGroups relacionados
CriticalityUsar con moderación, solo cuando tiene significado real (Estado, Límites)
Campos HiddenCampos de control calculados siempre con @UI.hidden: true
HeaderInfoElegir título y descripción significativos
AccionesAcciones importantes en lineItem, todas en identification
KPIsMáximo 3-4 KPIs en el header para claridad
ChartsMicro Charts para tendencias, charts completos en Facets propios
NavegaciónIntent-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.