L’Object Page affiche les informations detaillees d’un objet metier unique. Elle est le coeur des applications Fiori transactionnelles et permet d’afficher, modifier et gerer les donnees. Via les annotations CDS, le layout peut etre controle avec precision - des informations d’en-tete aux sections structurees jusqu’aux tables integrees.
Structure de l’Object Page
Une Object Page se compose de zones clairement definies :
+-----------------------------------------------------------------+| Object Page Header || +-------------------------+--------------------------------+ || | Object Image/ | Header Facets | || | Avatar | (KPIs, Status, Ratings) | || +-------------------------+--------------------------------+ || | Title: Commande 4711 | || | Subtitle: Client: Mustermann GmbH | || +----------------------------------------------------------+ |+------------------------------------------------------------------+| [Tab 1: General] [Tab 2: Postes] [Tab 3: Historique] |+------------------------------------------------------------------+| Section Content || +------------------------------------------------------+ || | Subsection: Donnees de base | || | +-------------------+-------------------+ | || | | Field Group 1 | Field Group 2 | | || | | - Champ A | - Champ D | | || | | - Champ B | - Champ E | | || | | - Champ C | - Champ F | | || | +-------------------+-------------------+ | || +------------------------------------------------------+ || +------------------------------------------------------+ || | Subsection: Postes (Table) | || | | Pos | Materiel | Qte | Prix | | || | |-----|----------|-----|------| | || | | 10 | MAT-001 | 5 | 100E | | || +------------------------------------------------------+ |+-----------------------------------------------------------------+Configuration de base de l’Object Page
Header Title et Subtitle
@UI.headerInfo: { typeName: 'Commande', typeNamePlural: 'Commandes', title: { type: #STANDARD, value: 'OrderId" }, description: { type: #STANDARD, value: 'CustomerName" }, imageUrl: 'ImageUrl' -- Optionnel: Image/Avatar}define view entity ZC_SalesOrder as projection on ZI_SalesOrder{ ...}Definir l’Object Page avec des Facets
@UI.facet: [ -- Header Facets { id: 'HeaderStatus', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'Status', position: 10 }, { id: 'HeaderTotal', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'TotalAmount', position: 20 },
-- Content Sections { id: 'GeneralSection', purpose: #STANDARD, type: #COLLECTION, label: 'General', position: 10 }, { id: 'ItemsSection', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Postes', position: 20, targetElement: '_Items" }]Sections multiples avec onglets
Plusieurs sections sont automatiquement affichees comme onglets lorsqu’elles sont definies au meme niveau.
Exemple : Layout a trois onglets
@UI.facet: [ -- Tab 1: General { id: 'GeneralTab', purpose: #STANDARD, type: #COLLECTION, label: 'General', position: 10 }, { id: 'GeneralBasic', parentId: 'GeneralTab', type: #FIELDGROUP_REFERENCE, targetQualifier: 'BasicData', label: 'Donnees de base', position: 10 }, { id: 'GeneralDates', parentId: 'GeneralTab', type: #FIELDGROUP_REFERENCE, targetQualifier: 'Dates', label: 'Echeances', position: 20 },
-- Tab 2: Postes { id: 'ItemsTab', purpose: #STANDARD, type: #COLLECTION, label: 'Postes', position: 20 }, { id: 'ItemsTable', parentId: 'ItemsTab', type: #LINEITEM_REFERENCE, label: 'Postes de commande', targetElement: '_Items', position: 10 },
-- Tab 3: Historique { id: 'HistoryTab', purpose: #STANDARD, type: #COLLECTION, label: 'Historique', position: 30 }, { id: 'ChangeLog', parentId: 'HistoryTab', type: #LINEITEM_REFERENCE, label: 'Journal des modifications', targetElement: '_ChangeLog', position: 10 }]Subsections dans les onglets
Pour des layouts plus complexes, les onglets peuvent avoir des subdivisions supplementaires :
@UI.facet: [ { id: 'GeneralTab', purpose: #STANDARD, type: #COLLECTION, label: 'General', position: 10 }, -- Subsection 1: Donnees d'en-tete { id: 'HeaderData', parentId: 'GeneralTab', type: #COLLECTION, label: 'Donnees d en-tete', position: 10 }, { id: 'HeaderFields', parentId: 'HeaderData', type: #FIELDGROUP_REFERENCE, targetQualifier: 'HeaderFields', position: 10 }, -- Subsection 2: Conditions { id: 'Conditions', parentId: 'GeneralTab', type: #COLLECTION, label: 'Conditions', position: 20 }, { id: 'ConditionFields', parentId: 'Conditions', type: #FIELDGROUP_REFERENCE, targetQualifier: 'ConditionFields', position: 10 }]Field Groups et layouts de formulaire
Les Field Groups organisent les champs en blocs logiques. Ils sont affiches comme des zones de formulaire.
Definition de Field Group
define view entity ZC_SalesOrder as projection on ZI_SalesOrder{ @UI.facet: [ { id: 'BasicDataSection', type: #COLLECTION, label: 'Donnees de base', position: 10 }, { id: 'OrderInfo', parentId: 'BasicDataSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'OrderInfo', label: 'Informations de commande', position: 10 }, { id: 'CustomerInfo', parentId: 'BasicDataSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'CustomerInfo', label: 'Informations client', position: 20 } ]
@UI.fieldGroup: [{ qualifier: 'OrderInfo', position: 10 }] @UI.identification: [{ position: 10 }] key OrderId,
@UI.fieldGroup: [{ qualifier: 'OrderInfo', position: 20 }] OrderDate,
@UI.fieldGroup: [{ qualifier: 'OrderInfo', position: 30 }] Status,
@UI.fieldGroup: [{ qualifier: 'CustomerInfo', position: 10 }] CustomerId,
@UI.fieldGroup: [{ qualifier: 'CustomerInfo', position: 20 }] CustomerName,
@UI.fieldGroup: [{ qualifier: 'CustomerInfo', position: 30 }] CustomerCity}Layout multi-colonnes
Les Field Groups sont affiches cote a cote par defaut. Pour un controle explicite :
@UI.facet: [ { id: 'TwoColumnSection', type: #COLLECTION, label: 'Deux colonnes', position: 10 }, { id: 'LeftColumn', parentId: 'TwoColumnSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'LeftFields', label: 'Colonne gauche', position: 10 }, { id: 'RightColumn', parentId: 'TwoColumnSection', type: #FIELDGROUP_REFERENCE, targetQualifier: 'RightFields', label: 'Colonne droite', position: 20 }]Tables inline dans l’Object Page
Les entites subordonnees peuvent etre affichees directement sous forme de tables dans l’Object Page.
Table LineItem standard
-- Dans l'entite racine@UI.facet: [{ id: 'ItemsSection', purpose: #STANDARD, type: #LINEITEM_REFERENCE, label: 'Postes', targetElement: '_Items', position: 20}]
-- Dans l'entite subordonnee (Items)define view entity ZC_SalesOrderItem as projection on ZI_SalesOrderItem{ @UI.lineItem: [{ position: 10 }] key OrderId,
@UI.lineItem: [{ position: 20 }] key ItemNumber,
@UI.lineItem: [{ position: 30 }] MaterialNumber,
@UI.lineItem: [{ position: 40 }] Quantity,
@UI.lineItem: [{ position: 50 }] @Semantics.amount.currencyCode: 'Currency" NetPrice,
@UI.lineItem: [{ position: 60 }] Currency}Table avec actions
define view entity ZC_SalesOrderItem as projection on ZI_SalesOrderItem{ @UI.lineItem: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'deleteItem', label: 'Supprimer' }, { type: #FOR_ACTION, dataAction: 'copyItem', label: 'Copier' } ] key ItemNumber, ...}Table inline editable
Pour les tables editables, la Behavior Definition doit etre configuree en consequence :
define behavior for ZI_SalesOrder alias SalesOrder{ ... association _Items { create; }}
define behavior for ZI_SalesOrderItem alias SalesOrderItem{ update; delete; field ( readonly ) OrderId, ItemNumber; field ( mandatory ) MaterialNumber, Quantity;}Custom Actions sur l’Object Page
Les actions peuvent etre placees dans le header, dans les sections ou au niveau des champs.
Actions d’en-tete
@UI.identification: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'approve', label: 'Approuver' }, { type: #FOR_ACTION, dataAction: 'reject', label: 'Rejeter' }, { type: #FOR_ACTION, dataAction: 'sendEmail', label: 'Envoyer email' }]key OrderId,Actions avec Criticality (code couleur)
@UI.identification: [{ type: #FOR_ACTION, dataAction: 'approve', label: 'Approuver', criticality: 'ApproveCriticality' -- Criticality dynamique}]key OrderId,
-- Champ calcule pour Criticality@UI.hidden: truecast( case Status when 'OPEN' then 3 -- Vert when 'PENDING' then 2 -- Jaune else 0end as abap.int1 ) as ApproveCriticality,Actions conditionnelles (Feature Control)
Les actions peuvent etre affichees/masquees en fonction des donnees :
-- Dans la Behavior Definitiondefine behavior for ZI_SalesOrder alias SalesOrder{ action approve result [1] $self;
// Feature Control determination setFeatures on modify { field Status; }}-- Dans l'implementation du BehaviorMETHOD get_instance_features. READ ENTITIES OF zi_salesorder IN LOCAL MODE ENTITY SalesOrder FIELDS ( Status ) WITH CORRESPONDING #( keys ) RESULT DATA(lt_orders).
LOOP AT lt_orders INTO DATA(ls_order). APPEND VALUE #( %tky = ls_order-%tky %action-approve = COND #( WHEN ls_order-Status = 'OPEN" THEN if_abap_behv=>fc-o-enabled ELSE if_abap_behv=>fc-o-disabled ) ) TO result. ENDLOOP.ENDMETHOD.Actions dans les sections
@UI.facet: [{ id: 'AdminSection', purpose: #STANDARD, type: #COLLECTION, label: 'Administration', position: 40}]
-- Groupe de champs avec actions@UI.fieldGroup: [{ qualifier: 'AdminActions', position: 10, type: #FOR_ACTION, dataAction: 'archive', label: 'Archiver"}]Adaptations de layout responsive
Fiori Elements adapte automatiquement le layout aux differentes tailles d’ecran. Un controle supplementaire est possible.
Importance pour l’adaptation responsive
@UI.lineItem: [{ position: 10, importance: #HIGH -- Toujours visible}]OrderId,
@UI.lineItem: [{ position: 20, importance: #MEDIUM -- Masque sur petits ecrans}]OrderDate,
@UI.lineItem: [{ position: 30, importance: #LOW -- Uniquement sur grands ecrans}]CreatedBy,Champs masques
Les champs peuvent etre completement masques :
@UI.hidden: trueInternalField,
-- Ou dynamiquement@UI.hidden: #( HideCondition )ConditionalField,Layout de colonnes FieldGroup
@UI.fieldGroup: [{ qualifier: 'Details', position: 10, label: 'Numero de commande"}]@EndUserText.label: 'Numero de commande"OrderId,Configurer les Header Facets
DataPoint dans le header
@UI.facet: [{ id: 'HeaderStatus', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'StatusDP', position: 10}]
@UI.dataPoint: { qualifier: 'StatusDP', title: 'Statut', criticality: 'StatusCriticality"}Status,Indicateur de progression
@UI.dataPoint: { qualifier: 'Completion', title: 'Achevement', visualization: #PROGRESS, targetValue: 100}CompletionPercent,Indicateur de notation
@UI.dataPoint: { qualifier: 'CustomerRating', title: 'Note client', visualization: #RATING, targetValue: 5}Rating,Contact Card dans le header
@UI.facet: [{ id: 'ContactCard', purpose: #HEADER, type: #CONTACT_REFERENCE, targetElement: '_Contact', position: 30}]Exemple complet
@EndUserText.label: 'Sales Order - Projection"@Metadata.allowExtensions: true@UI.headerInfo: { typeName: 'Commande', typeNamePlural: 'Commandes', title: { type: #STANDARD, value: 'OrderId' }, description: { type: #STANDARD, value: 'CustomerName' }}define view entity ZC_SalesOrder as projection on ZI_SalesOrder{ @UI.facet: [ -- Header Facets { id: 'HeaderStatus', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'Status', position: 10 }, { id: 'HeaderTotal', purpose: #HEADER, type: #DATAPOINT_REFERENCE, targetQualifier: 'TotalAmount', position: 20 },
-- Tab 1: General { id: 'GeneralTab', purpose: #STANDARD, type: #COLLECTION, label: 'General', position: 10 }, { id: 'OrderData', parentId: 'GeneralTab', type: #FIELDGROUP_REFERENCE, targetQualifier: 'OrderData', label: 'Donnees de commande', position: 10 }, { id: 'CustomerData', parentId: 'GeneralTab', type: #FIELDGROUP_REFERENCE, targetQualifier: 'CustomerData', label: 'Donnees client', position: 20 },
-- Tab 2: Postes { id: 'ItemsTab', purpose: #STANDARD, type: #COLLECTION, label: 'Postes', position: 20 }, { id: 'ItemsTable', parentId: 'ItemsTab', type: #LINEITEM_REFERENCE, targetElement: '_Items', label: 'Postes de commande', position: 10 },
-- Tab 3: Notes { id: 'NotesTab', purpose: #STANDARD, type: #COLLECTION, label: 'Notes', position: 30 }, { id: 'NotesField', parentId: 'NotesTab', type: #FIELDGROUP_REFERENCE, targetQualifier: 'Notes', position: 10 } ]
@UI.identification: [ { position: 10 }, { type: #FOR_ACTION, dataAction: 'approve', label: 'Approuver' }, { type: #FOR_ACTION, dataAction: 'reject', label: 'Rejeter' } ] @UI.fieldGroup: [{ qualifier: 'OrderData', position: 10 }] key OrderId,
@UI.dataPoint: { qualifier: 'Status', title: 'Statut', criticality: 'StatusCriticality' } @UI.fieldGroup: [{ qualifier: 'OrderData', position: 20 }] Status,
@UI.hidden: true StatusCriticality,
@UI.fieldGroup: [{ qualifier: 'OrderData', position: 30 }] OrderDate,
@UI.dataPoint: { qualifier: 'TotalAmount', title: 'Montant total' } @UI.fieldGroup: [{ qualifier: 'OrderData', position: 40 }] @Semantics.amount.currencyCode: 'Currency" TotalAmount,
Currency,
@UI.fieldGroup: [{ qualifier: 'CustomerData', position: 10 }] CustomerId,
@UI.fieldGroup: [{ qualifier: 'CustomerData', position: 20 }] CustomerName,
@UI.fieldGroup: [{ qualifier: 'CustomerData', position: 30 }] CustomerCity,
@UI.fieldGroup: [{ qualifier: 'Notes', position: 10 }] @UI.multiLineText: true Notes,
-- Associations _Items : redirected to composition child ZC_SalesOrderItem}Bonnes pratiques
| Aspect | Recommandation |
|---|---|
| Nombre d’onglets | Maximum 5-7 onglets pour la clarte |
| Field Groups | Regroupement logique, max. 6-8 champs par groupe |
| Header Facets | Uniquement les KPIs les plus importants, max. 4-5 facets |
| Actions | Actions principales proeminentes, secondaires dans le menu |
| Tables inline | Activer la pagination pour les grands volumes de donnees |
Resume
L’Object Page offre de nombreuses possibilites de personnalisation via les annotations CDS :
- Header : Title, Subtitle, DataPoints, Contact Cards
- Sections/Tabs : Organisation structuree du contenu avec
#COLLECTION - Field Groups : Layout de formulaire avec
#FIELDGROUP_REFERENCE - Tables inline : Entites subordonnees avec
#LINEITEM_REFERENCE - Actions : Header-Actions, Section-Actions, Table-Actions
- Responsive :
importancepour les layouts adaptatifs
La combinaison de ces elements permet des vues de detail professionnelles sans code UI5.