Serving Fiori UIs
CAP provides out-of-the-box support for SAP Fiori Elements frontends.
This guide explains how to add one or more SAP Fiori Elements apps to a CAP project, how to add SAP Fiori Elements annotations to respective service definitions, etc. In the following sections, we are always referring to SAP Fiori Elements when mentioning Fiori.
Learn more about developing SAP Fiori Elements and OData V4 (since 1.84.)
Content
Adding SAP Fiori Apps to CAP Projects
As showcased in cap/samples, SAP Fiori apps should be added as sub folders to the app/
of a CAP project. Each sub folder constitutes an individual SAP Fiori application, with local annotations, manifest.json
, etc. So, a typical folder layout would look like this:
Folder / Sub Folder | Description |
---|---|
app/ |
all SAP Fiori apps should go in here |
browse/ |
SAP Fiori app for end users |
orders/ |
SAP Fiori app for order management |
admin/ |
SAP Fiori app for admins |
index.html |
For sandbox tests |
srv/ |
all services |
db/ |
domain models, and db stuff |
By Copying from cap/samples
For example, you can copy the SAP Fiori apps from cap/samples as a template and modify the content as appropriate.
Adding SAP Fiori Annotations
The main content to be added is service definitions annotated with information how to render respective data.
What Are SAP Fiori Annotations?
SAP Fiori elements apps are generic frontends, which construct and render the pages and controls based on annotated metadata documents. The annotations provide semantic annotations used to render such content, for example:
annotate CatalogService.Books with @(
UI: {
SelectionFields: [ ID, price, currency_code ],
LineItem: [
{Value: title},
{Value: author, Label:'{i18n>Author}'},
{Value: genre.name},
{Value: price},
{Value: currency.symbol, Label:' '},
]
}
);
Find this source and many more in cap/samples. Learn more on OData Annotations in CDS.
Where to Put Them?
While CDS in principle allows to add such annotations everywhere in your models, we recommend putting them in separate .cds files placed in your ./app/* folders, for example, as follows.
./app #> all your fiori annotations should go here, for example:
./admin
fiori-service.cds #> annotating ../srv/admin-service.cds
./browse
fiori-service.cds #> annotating ../srv/cat-service.cds
index.cds
./srv #> all service definitions should stay clean in here:
admin-service.cds
cat-service.cds
...
See this also in cap/samples/fiori.
Reasoning: This recommendation essentially follows best practices and guiding principles of Conceptual Modeling and Separation of Concerns.
Maintaining Annotations
Maintaining OData annotations in .cds files is accelerated by the SAP Fiori tools - CDS OData Language Server @sap/ux-cds-odata-language-server-extension comprised in SAP CDS language support plugin. It assists you with adding and editing OData annotations in CDS syntax with:
- Code Completion
- Validation against the OData vocabularies and project metadata
- Navigation to the referenced annotations
- Quick view of vocabulary information
- Internationalization support
These assisting features are provided for OData annotations in CDS syntax and can’t be used yet for the core data services common annotations.
@sap/ux-cds-odata-language-server-extension module doesn’t require any manual installation. The latest version is fetched by default from npmjs.com as indicated in the user preference setting CDS > Contributions: Registry.
Learn more about the *CDS extension for VS Code.
Code Completion
CDS OData Language Server provides a list of context-sensitive suggestions based on the service metadata and OData vocabularies. You can use it to choose OData annotation terms, their properties, and values from the list of suggestions in annotate directives applied to service entities and entity elements. See annotate directives for more details.
Using Code Completion
To trigger the code completion, press Ctrl + Space (Windows), CMD + Space (Mac). The list of suggested values is displayed.
You can filter the list of suggested values by typing more characters.
Navigate to the desired value using up or down arrows or your mouse. Accept the highlighted value using Enter key or a mouse click. Use code completion to add and change individual values (word-based completion) and to add small code blocks containing annotation structures along with mandatory properties (micro-snippets). In an active code snippet, you can use Tab to quickly move to the next tab stop.
Example: Annotating Service Entities
(cursor position indicated by |
)
-
Place cursor in the
annotate
directive for a service entity, for exampleannotate Foo.Bar with ;
and trigger the code completion. -
Type
u
to filter the suggestions and choose{} UI
. Micro-snippet@UI : {|}
is inserted:annotate Foo.Bar with @UI : {|};
-
Use the code completion again to add an annotation term from UI vocabulary, in this example
SelectionFields
. Micro snippet for this annotation is added and the cursor is placed right after the term name letting you define a qualifier:annotate Foo.Bar with @UI : {SelectionFields | : []};
-
Press the Tab key to move the cursor to the next tab stop and use the code completion again to add values. As
UI.SelectionFields
annotation is a collection of entity elements (entity properties), all elements of the annotated entity are suggested.To choose an element of an associated entity, first select the corresponding association from the list and type . (period). Elements of associated entity are suggested.
You can add multiple values separated by comma.
annotate Foo.Bar with @UI : { SelectionFields : [ description, assignedIndividual.lastName| ], };
-
Add a new line after , (comma) and use the code completion again to add another annotation from UI vocabulary, such as
LineItem
. Line item is a collection ofDataField
records. To add a record, select the record type you need from the completion list.annotate Foo.Bar with @UI : { SelectionFields : [ description, assignedIndividual.lastName ], LineItem : [ { $Type:'UI.DataField', Value : |, }, ] };
For each record type, two kinds of micro-snippets are provided: one containing only mandatory properties and one containing all properties defined for this record (full record). Usually you need just a subset of properties. So, you either select a full record and then remove the properties you don’t need, or add the record containing only required properties and then add the remaining properties.
-
Use code completion to add values for the annotation properties.
annotate Foo.Bar with @UI : { SelectionFields : [ description, assignedIndividual.lastName ], LineItem : [ { $Type:'UI.DataField', Value : description, }, { $Type:'UI.DataFieldForAnnotation', Target : 'assignedIndividual/@Communication.Contact', },| ] };
To add values pointing to annotations defined in another CDS source, you must reference this source with
using
directive. See Theusing
Directive for more details.
Example: Annotating Entity Elements
(cursor position indicated by |
)
-
Place the cursor in the
annotate
directive, for exampleannotate Foo.Bar with {|};
, add a new line and trigger the code completion. You get the list of entity elements. Choose the one you want to annotate.annotate Foo.Bar with { code| };
-
Press Space and use the code completion again and choose
{} UI
. Micro-snippet@UI : {|}
is inserted:annotate Foo.Bar with { code @UI : { | } };
-
Trigger completion again and choose an annotation term from UI vocabulary, in this example: Hidden.
annotate Foo.Bar with { code @UI : {Hidden : |} };
-
Press the Tab key to move the cursor to the next tab stop and use the code completion again to add the value. As
UI.Hidden
annotation is of Boolean type, the values true and false is suggested:annotate Foo.Bar with { code @UI : {Hidden : false } };
Diagnostics
CDS OData Language Server server validates OData annotations in .cds files against the service metadata and OData vocabularies. It also checks provided string content for language-dependent annotation values and warns you if the format doesn’t match the internationalization (i18n) key reference. It makes you aware that this string is hard-coded and won’t change based on the language setting in your application. See Internationalization support for more details.
Validation is performed when you open a .cds file and then is retriggered with each change to the relevant files.
You can view the diagnostic messages by hovering over the highlighted part in the annotation file or by opening the problems panel. Click on the message in problems panel to navigate to the related place in the annotation file.
If an annotation value points to the annotation defined in another CDS source, you must reference this source with
using
directive to avoid warnings. See Theusing
Directive for more details.
Navigation to Referenced Annotations
CDS OData Language Server enables quick navigation to the definition of referenced annotations. For example, if your annotation file contains a DataFieldForAnnotation
record referencing an Identification
annotation defined in the service file, you can view which file it’s defined in and what fields or labels this annotation contains. You can even update the Identification
annotation or add comments.
You can navigate to the referenced annotation using Peek Definition and Go To Definition features.
If the referenced annotation is defined in another CDS source, you must reference this source with
using
directive to enable the navigation. See Theusing
Directive for more details.
Peek Definition
Peek Definition lets you preview and update the referenced annotation without switching away from the code that you’re writing. It’s triggered when your cursor is inside the referenced annotation value.
- Using keyboard: press Alt + F12 (Windows), Option F12 (Mac)
- Using mouse: right-click and select Peek Definition If an annotation is defined in multiple sources, all these sources are listed. You can select which one you would like to view or update. Annotation layering isn’t considered.
Go to Definition
Go To Definition lets you navigate to the source of the referenced annotation and opens the source file scrolled to the respective place in a new tab. It’s triggered when your cursor is inside the referenced annotation value.
Place your text cursor inside the path referencing the annotation term segment or translatable string value and trigger Go to Definition:
- Using keyboard: press F12 in VS Code, Ctrl + F11 in SAP Business Application Studio
- Using mouse: right-click and select Go To Definition
- Using keyboard and mouse: Ctrl + mouse click (Windows), CMD + mouse click (Mac)
If an annotation is defined in multiple sources, Peek definition listing these sources will be shown instead. Annotation layering isn’t considered.
Documentation (Quick Info)
Annotation language server provides a quick information for annotation terms, record types, and properties used in the annotation file or provided as suggestions in code completion lists. This information is retrieved from the respective OData vocabularies and can provide answers to the following questions:
- What is the type and purpose of the annotation term / record type / property?
- What targets can the annotation term apply to?
- Is the annotation term / record type / property experimental? Is it deprecated?
- Is this annotation property mandatory or optional?
The exact content depends on the availability in OData vocabularies.
To view the quick info for an annotation term, record type or property used in the annotation file, hover your mouse over it. The accompanying documentation is displayed in a hover window, if provided in the respective OData vocabularies.
To view the quick info for each suggestion in the code completion list, either press Ctrl + Space (Windows), CMD + Space (Mac) or click the info icon. The accompanying documentation for the suggestion expands to the side. The expanded documentation stays so and updates as you navigate the list. You can close this by pressing Ctrl + Space / CMD + Space again or by clicking on the close icon.
Internationalization Support
When you open an annotation file, all language-dependent string values are checked against the i18n.properties file. Each value that doesn’t represent a valid reference to the existing text key in the i18n.properties file, is indicated with a warning. A Quick Fix action is suggested to generate a text key in i18n file and substitute your string value with the reference to that entry.
Prefer @title
and @description
Influenced by JSON Schema, CDS supports the common annotations @title
and @description
, which are mapped to corresponding OData annotations as follows:
CDS | JSON Schema | OData |
---|---|---|
@title |
title |
@Common.Label |
@description |
description |
@Core.Description |
It’s recommended to prefer these annotations over the OData ones in protocol-agnostic data models and service models, for example:
annotate my.Books with { //...
title @title: 'Book Title';
author @title: 'Author ID';
}
Prefer @readonly
, @mandatory
, …
CDS supports @readonly
as a [common annotation, which translates to respective OData annotations from the @Capabilities
vocabulary. We recommend using the former for reasons of conciseness and comprehensibility as shown in this example:
@readonly entity Foo { // entity-level
@readonly foo ... // element-level
}
is equivalent to:
entity Foo @(Capabilities:{
// entity-level
InsertRestrictions.Insertable: false,
UpdateRestrictions.Updatable: false,
DeleteRestrictions.Deletable: false
}) {
// element-level
@Core.Computed foo ...
}
Similar recommendations apply to @mandatory
and others → see Common Annotations.
Draft-Based Editing
SAP Fiori supports edit sessions with draft states stored on the server, so users can interrupt and continue later on, possibly from different places and devices. CAP as well as SAP Fiori Elements provide out-of-the-box support for Drafts as outlined in the following sections. It’s recommended to always use Draft when your application needs data input by end users.
For details and guidelines about that, see SAP Fiori Design Guidelines for Draft.
Find a working end-to-end version in cap/samples/fiori.
Enabling Draft with @odata.draft.enabled
To enable draft for an entity exposed by a service, simply annotate it with @odata.draft.enabled
as in this example:
annotate AdminService.Books with @odata.draft.enabled;
Enabling Draft for Localized Data
Additionally annotate the underlying base entity in the base model with @fiori.draft.enabled
to also support drafts for localized data:
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
Background: SAP Fiori Drafts requires single keys of type
UUID
, which isn’t the case by default for the automatically generated_texts
entities (→ see the Localized Data guide for details). The annotation@fiori.draft.enabled
tells the compiler to add such a technical primary key element namedID_texts
.
Validating Drafts
You can add custom handlers to add specific validations as usual. In addition, in case of draft, you can register handlers to the PATCH
events to validate input per field, during the edit session, as follows.
… in Java
You can add your validation logic in before operation event handlers. Specific events for draft operations exist, see Java > Fiori Drafts > Editing Drafts for more details.
… in Node.js
You can add your validation logic in the before operation handler for the CREATE
or UPDATE
event (as in the case of nondraft implementations) or on the SAVE
event (specific to drafts only):
srv.before ('CREATE','Books', (req)=>{ ... }) // run before create
srv.before ('UPDATE','Books', (req)=>{ ... }) // run before create
srv.before ('SAVE','Books', (req)=>{...}) // run at final save only
In addition, you can add field-level validations on the individual PATCH
events:
srv.before ('PATCH','Books', (req)=>{...}) // run during editing
These get triggered during the draft edit session whenever the user tabs from one field to the next, and can be used to provide early feedback.
Value Help Support
In addition to supporting the standard @Common.ValueList
annotations as defined in the OData Vocabularies, CAP provides advanced convenient support for Value Help as understood and supported by SAP Fiori.
Convenience option @cds.odata.valuelist
Simply add annotation @cds.odata.valuelist
to an entity, and all Associations targeting this entity will automatically receive Value Lists in SAP Fiori clients. For example:
@cds.odata.valuelist
entity Currencies {}
service BookshopService {
entity Books { //...
currency : Association to Currencies;
}
}
Pre-Defined Types in @sap/cds/common
The reuse types in @sap/cds/common already have this added to base types and entities, so all uses automatically benefit from this. This is an effective excerpt of respective definitions in @sap/cds/common
:
type Currencies : Association to sap.common.Currencies;
context sap.common {
entity Currencies : CodeList {...};
entity CodeList { name : localized String; ... }
}
annotate sap.common.CodeList with @(
UI.Identification: [name],
cds.odata.valuelist,
);
Usages of @sap/cds/common
In effect, usages of @sap/cds/common stay clean of any pollution, for example:
using { Currency } from '@sap/cds/common';
entity Books { //...
currency : Currency;
}
Find that also in our cap/samples.
Still, all SAP Fiori UIs on all services exposing Books
will automatically receive Value Help for currencies. You can also benefit from that when deriving your project-specific code list entities from sap.common.CodeList.
Resulting Annotations in EDMX
Here is an example how this ends up as OData Common.ValueList
annotations:
<Annotations Target="AdminService.Books/currency_code">
<Annotation Term="Common.ValueList">
<Record Type="Common.ValueListType">
<PropertyValue Property="CollectionPath" String="Currencies"/>
<PropertyValue Property="Label" String="Currency"/>
<PropertyValue Property="Parameters">
<Collection>
<Record Type="Common.ValueListParameterInOut">
<PropertyValue Property="ValueListProperty" String="code"/>
<PropertyValue Property="LocalDataProperty" PropertyPath="currency_code"/>
</Record>
<Record Type="Common.ValueListParameterDisplayOnly">
<PropertyValue Property="ValueListProperty" String="name"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</Annotation>
Test
- See OData (Open Data Protocol) for more details.
- See OData Annotations for more details.