My CDS view self study tutorial – Part 5 how to create CDS view which supports navigation in OData service
- How is navigation implemented among CDS views
- How to test navigation
- How the navigation is implemented in ABAP side
The series of my CDS view blogs
- Part1 – how to test odata service generated by CDS view
- Part2 – what objects are automatically generate after you activate one CDS view
- Part3 – how is view source in Eclipse converted to ABAP view in the backend
- Part4 – how does annotation @OData.publish work
- Part5 – this blog
- Part6 – consume table function in CDS view
- Part7 – unveil the secret of @ObjectModel.readOnly
- Part8 – my summary of different approaches for annotation declaration and generation
- Part9 – cube view and query view
- Part10 – How does CDS view key user extensibility work in S4/HANA
- Part11 – CDS view test double framework
- Part12 – CDS view source code count tool
- Part13 – CDS view authorization
So far we have a working CDS view ready for us to create a UI5 application on top of it via Smart Template in WebIDE within just a couple of minutes. Once done, the UI5 application will display the data from our CDS view like below. For step by step how to achieve this, please refer to this blog: Step by Step to create CDS view through SmartTemplate + WebIDE .
How is navigation implemented among CDS views
In this part, let’s create CDS view which supports node navigation in OData service. The previous CDS view we created has a flat structure which only have a root node. Now let’s create a series of CDS views:
1. A CDS view which contains two fields: spfli.connid and spfli.carrid. This view acts as the root node of the corresponding OData service model from semantic point of view. This view can support navigation from itself to the defined children node.
2. A CDS view which acts as the navigation target from previously defined “root” view. Besides the two fields from sflight.connid and sflight.carrid which correspond to the root view, it has additional new field sflight.fldate.
OData navigation means suppose currently I am in the context of spfli.connid = 0001 and spfli.carrid ( data record with yellow ), and through navigation I can get all its dependent data in red color. We will see how this navigation would be performed later.
3. A CDS view which exposes the two fields connid and carrid from root view and the associated data from child view.
This view is called “consumption” view and used to published as OData service.
Source code of view #1:
@AbapCatalog.sqlViewName: 'zspfliroot'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'root view'
define view Zspfli_Root as select from spfli
association [0..*] to Zsflight_Child as _Item on $projection.carrid = _Item.carrid
and $projection.connid = _Item.connid
{
key spfli.connid,
key spfli.carrid,
@ObjectModel.association.type: #TO_COMPOSITION_CHILD
_Item
}
Source code of view #2:
@AbapCatalog.sqlViewName: 'zsflightchild'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'child_view'
define view Zsflight_Child as select from sflight
association [1..1] to zspfli_root as _root
on $projection.connid = _root.connid
and $projection.carrid = _root.carrid
{
key sflight.carrid,
key sflight.connid,
key sflight.fldate,
@ObjectModel.association.type: [#TO_COMPOSITION_ROOT, #TO_COMPOSITION_PARENT]
_root
}
Source code of view #3:
@AbapCatalog.sqlViewName: 'zflight_c'
@AbapCatalog.compiler.compareFilter: true
@AccessControl.authorizationCheck: #CHECK
@EndUserText.label: 'flight consumption view'
@OData.publish: true
@ObjectModel: {
type: #CONSUMPTION,
compositionRoot,
createEnabled,
deleteEnabled,
updateEnabled
}
define view Zflight_Com as select from Zspfli_Root {
key Zspfli_Root.carrid,
key Zspfli_Root.connid,
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD]
Zspfli_Root._Item
}
Activate all of these three CDS views. Since the third consumption view has annotation @OData.publish: true, once activated there will be an OData service automatically generated:
How to test navigation
First check the response from OData metadata request via url /sap/opu/odata/sap/ZFLIGHT_COM_CDS/$metadata in gateway client.
You should find two AssociationSets generated based on corresponding annotation in CDS views.
The entityset Zflight_Com has type Zflight_ComType, which has the navigation Property “to_Item”. Now we can test the navigation.
First we get the root node’s content via url: /sap/opu/odata/sap/ZFLIGHT_COM_CDS/Zflight_Com(connid=’0400′,carrid=’LH’) .
And in the response, we are told that the correct url for navigation from current node to its child node is just to append the navigation property defined in metadata, toItem, to the end of url, that is, /sap/opu/odata/sap/ZFLIGHT_COM_CDS/Zflight_Com(connid=’0400′,carrid=’LH’)/to_Item .
How the navigation is implemented in ABAP side
Set the breakpoint in the method below and re-trigger the navigation operation.
Check the generated SQL statement in variable statement in line 27.
SELECT "Zsflight_Child"."CARRID" AS "CARRID", "Zsflight_Child"."CONNID" AS "CONNID", "Zsflight_Child"."FLDATE" AS "FLDATE" FROM "ZSFLIGHTCHILD" AS "Zsflight_Child"
WHERE "Zsflight_Child"."CARRID" = ? AND "Zsflight_Child"."CONNID" = ? AND "Zsflight_Child"."MANDT" = '001' WITH PARAMETERS( 'LOCALE' = 'CASE_INSENSITIVE' )
The value for two placeholders ( ? ) are stored in me->parameters->param_tab:
And check response in et_flag_data:
Hi,
This blog helps me a lot on CDS navigation development.
However, when I association the root-child views with cardinality [0..*], one ATC error raised.
" Key of target view fully defined in on-condition but maximum target cardinality of association not equal 1".
Do you have any idea solving this error?
Thanks and regards,
Avery
Hi Avery,
is it possible that you didn't define sflight.fldate as key field in your child view?
Best regards,
Sebastian
Jerry,
Thanks for your super informative blogs..Very impressive.
Question for you...If you add the annotation @DefaultAggregation: #SUM to the root view (pretending you had a numeric field there), It is my experience that the KEY of the root CDS view will change to a Generated_ID, not the key you specify. Due to this new key, the navigation via Associations does not work.
Have you experienced this? Do you have any idea how to handle that (short of using SEGW)?
Thanks,
Tim
p.s. I have an app that I really need to have totals, and navigate to the details (old fashioned ALV + Hotspot replication).
Hi Jerry ,
I followed the above steps for the navigation but while activating i am getting error. can you please let me know how you activated cds1 and cds 2 as they are using Zsflight_Child and zspfli_root respectively .but this are new cds and using one another so while activating i am getting error as Zsflight_Child and zspfli_root are not active in the dictionary.
Try creating them without the association part first. As soon as you created and activated them, add the associations.
That worked for me
Regards, Tobias
You have to activate them together.
Hi Guys,
I have a doubt... why do we need the 3rd view for exposing the data to Odata? why can't we expose the Root view itself to OData?
Regards, Arun
we always use to expose the consumption view . For navigation we require two entity sets which cannot be handled via root views. so we created a third view to expose as odata service which can handle navigation.
Regards,
Vikash
Thank you very much Jerry for this nice blog.
it is really interesting, but how can I debug the CDS view? .. When you have entity header/to_item . , in ABAP , we can debug our select statement but could we do it in CDS?
Regards
Ebrahim
CDS views are like it says just the views so you cannot debug. However OData implementations are ABAP so any OData created from CDS views you can debug and view the entities.
Nice example and nice blog. But nevertheless I still do not understand why we need the OData service(in this case created via annotation) to be created from CONSUMPTION CDS view. If I repeat exactly the steps in the metadata we have 2 entities which are with one and the same content , the root entity and the consumption entity. The root entity even does not have a navigation to the child one.
Therefore I would not use the consumption but would have the Root CDS view to hold the OData publish annotation to true. Thus we will not have duplicate data in the metadata.
What about that?
Navigation features are used in UI5/Fiori Application which consumes the OData. Therefore you need to create the OData. The navigation feature that he's explaining is also using the XML output of the OData. Please check the section that below,
OData metadata request via url /sap/opu/odata/sap/ZFLIGHT_COM_CDS/$metadata in gateway client.
However for Restful ABAP, these are all automazically done. Please check out the RAP which also explains about the consumption views.
Thank you very much Jerry for this nice blog.
I have one question for the same topic navigation, Let’s say I have three level hierarchy
—A(Root)
——-B(Child Of A)
————–C(Child of B)
How we can define navigation for the same. I tried but not successful, If you have any example for the above case, Kindly share.
Thanks,
Hi Sandeep,
You can achieve this by defining the below annotations
Annotation for C
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT] B
Annotation for B
@ObjectModel.association.type: [#TO_COMPOSITION_CHILD] C
@ObjectModel.association.type: [#TO_COMPOSITION_PARENT,#TO_COMPOSITION_ROOT] A
Regards
Roopa