##はじめに
この記事は、UIアノテーションの使い方について学ぶシリーズの2回目です。
1回目の記事→【Fiori】UIアノテーションの定義を知る
もともとは以下の流れで考えていましたが、
①WebIDEのアノテーションモデラでアノテーションをつけてみる
②CDSビューに直接つける方法を確認する
順番を入れ替えて以下の順序とします。
①CDSビューにアノテーションをつける
②WebIDEのアノテーションモデラで結果を確認する
Web IDEにはローカルアノテーションファイルをバックエンドにMetadata Extensionとして書き出す機能があるのですが、ベータ版ということで(?)うまく使えなかったので、計画を変更しました。このAnnotation Pushdownという機能については、2019年のUI5conで紹介されていました。
UI5con@SAP 2019: SAPUI5 Visual Editor for Fiori Elements Application Development
##ゴール
以下のようなList ReportをCDS + UIアノテーションで作ります。List Reportで使うであろう機能をなるべく入れ込んでみました。
①フィルタ
②テーブル
③テーブルのタイトル
④検索ヘルプ
⑤ドロップダウンリスト
⑥アクション
⑦他アプリへのナビゲーション
##構成
Sales Order、Sales Order Itemをベースにした親子関係のあるデータモデルを作成します。
※以下の図では検索ヘルプ用のアソシエーションは省略しています。
##ステップ
- テーブルの定義
- Interface Viewの定義
- Consumption Viewの定義
- ドロップダウン用のView定義
- Metadata Extensionの定義
ステップ1~4については、【Fiori】ElementsアプリのためのCDS + BOPFをご参照ください。
4まで終わったら、WebIDEのList Reportテンプレートを使用してアプリを作成しておきます。
##Metadata Extensionの定義
今回のメインである、UIアノテーションについて見ていきます。List Reportに表示するのはヘッダなので、ヘッダのConsumption Viewにアノテーションをつけます。
UIアノテーションをつけていない状態は以下のようになります。
Basic Searchの項目は、UIアノテーションではなく@Search.searchable: true
+ @Search.defaultSearchElement: true
のアノテーションによって表示されます。
また、Editing Statusの項目は、ドラフトを有効化することにより表示されます。
###①フィルタ、②テーブル
まずは、フィルタ条件とテーブル列が画面に表示されるようにMetadata Extensionにアノテーションを追加します。
※Metadata Extensionを変更したら、ヘッダのConsumption Viewも有効化するようにします。そうしないとアノテーションモデルに反映されません。
####Metadata Extension
フィルタ条件を表示するには、@UI.selectionField
を使用します。positionで表示位置を指定します。
テーブルの列を表示するには、@UI.lineItem
を使用します。フィルタと同様、posisionで表示位置を指定します。importance: #HIGH
は画面サイズが小さくなっても必ず表示したい項目に設定します。positionは後から項目を追加しやすいように10刻みにするのが慣例のようです。
@Metadata.layer: #CORE
annotate view Z_C_SO_MOB67
with
{
@UI: {
selectionField: [{position: 10 }],
lineItem: [{ position: 10, importance: #HIGH }]
}
SoId;
@UI: {
selectionField: [{position: 20 }],
lineItem: [{ position: 20, importance: #HIGH }]
}
CreatedAt;
@UI: {
lineItem: [{ position: 30, importance: #HIGH }]
}
CreatedBy;
@UI: {
selectionField: [{position: 30 }],
lineItem: [
{ position: 40, importance: #HIGH }
]
}
BpId;
@UI: {
selectionField: [{ position: 40 }]
Currency;
@UI: {
lineItem: [{ position: 50, importance: #MEDIUM }]
}
GrossAmount;
@UI: {
selectionField: [{ position: 50 }],
lineItem: [{ position: 60, importance: #MEDIUM }]
}
DeliveryStatus;
@UI: {
lineItem: [{ position: 70, importance: #MEDIUM }]
}
OverallStatus;
}
####アノテーションモデル
CDSビューのアノテーションがアノテーションモデルにどのように反映されたか見てみます。List Reportのmanifest.jsonをDescriptor Editorで開き、"Sync Metadata"を押してアノテーションモデルを同期させます。
localServiceの下の..._CDS_VAN.xmlというファイルを開きます。
以下のような部分が見つかります。SelectionFieldsもLineItemもCollection系の部品(UI Vocabulary参照)なので、内側に項目を列挙する形になっています。
<Annotations Target="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67Type">
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="SoId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedAt"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedBy"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="BpId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="GrossAmount"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="DeliveryStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="OverallStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
</Collection>
</Annotation>
<Annotation Term="UI.SelectionFields">
<Collection>
<PropertyPath>SoId</PropertyPath>
<PropertyPath>CreatedAt</PropertyPath>
<PropertyPath>BpId</PropertyPath>
<PropertyPath>Currency</PropertyPath>
<PropertyPath>DeliveryStatus</PropertyPath>
</Collection>
</Annotation>
</Annotations>
対してCDSビューは、各項目の上にアノテーションと出現位置を指定するようになっていました。項目が多いとpositionが正しく振れたかわからなくなりそうですね。。。
@UI: {
selectionField: [{position: 10 }],
lineItem: [{ position: 10, importance: #HIGH }]
}
SoId;
####Metadata Extension
テーブルのタイトルには@UI.headerInfo.typeNamePlural
を使用します。@UI.headerInfoはannotate View
よりも上に置く、珍しいアノテーションです。
@Metadata.layer: #CORE
@UI: {
headerInfo: {
typeNamePlural: 'Sales Orders'
}
}
annotate view Z_C_SO_MOB67
with
{
####アノテーションモデル
アノテーションモデルには以下のように反映されます。
<Annotation Term="UI.HeaderInfo">
<Record><PropertyValue Property="TypeName" String="Sales Order Header"/><PropertyValue Property="TypeNamePlural" String="Sales Orders"/></Record>
</Annotation>
###④検索ヘルプ
Customerという項目に検索ヘルプを付けます。
####CDSビュー
検索ヘルプはCDSのAssociationによって実現します。よってMetadata ExtensionではなくCDSビュー本体につけます。
※Associationで使用しているビューの定義はこちらをご参照ください。
define view Z_C_SO_MOB67
as select from Z_I_SO_MOB67
association [0..*] to Z_C_SOI_MOB67 as _Item on $projection.UUID = _Item.ParentUUID
association [0..1] to Z_C_BPA_MOB67 as _Customer on $projection.BpId = _Customer.BpId //検索ヘルプ用のAssociation
{
key node_key as UUID,
@Search.defaultSearchElement: true
so_id as SoId,
created_by as CreatedBy,
created_at as CreatedAt,
changed_by as ChangedBy,
changed_at as ChangedAt,
@Consumption.valueHelp: '_Customer' //検索ヘルプを設定
bp_id as BpId,
...
_Customer //Associationを公開
####メタデータ
メタデータを同期させた後で、metadata.xmlファイルを見てみます。
BpIdにsap:value-list="standard"
というアノテーションがついています。
<Property Name="BpId" Type="Edm.String" MaxLength="10" sap:display-format="UpperCase" sap:label="Customer"
sap:quickinfo="EPM: Business Partner ID" sap:value-list="standard"/>
下の方にいくと、Common.ValueList
というアノテーションもついています。こちらは検索ヘルプの定義です。
<Annotations Target="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67Type/BpId" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Common.ValueList">
<Record><PropertyValue Property="Label" String="Supplier Consumption View"/><PropertyValue Property="CollectionPath" String="Z_C_BPA_MOB67"/><PropertyValue Property="SearchSupported" Bool="false"/>
<PropertyValue Property="Parameters">
<Collection>
<Record Type="Common.ValueListParameterInOut"><PropertyValue Property="LocalDataProperty" PropertyPath="BpId"/><PropertyValue Property="ValueListProperty" String="BpId"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="BpRole"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="Email"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="Phone"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="Fax"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="Web"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="CompanyName"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="Currency"/></Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
###⑤ドロップダウンリスト
Delivery Statusをドロップダウンで選択できるようにします。
####CDSビュー
ドロップダウンリストは検索ヘルプの派生形です。検索用のCDSビューで、@ObjectModel.resultSet.sizeCategory: #XS
というアノテーションをつけます。
検索用のCDSビュー
@AbapCatalog.sqlViewName: 'ZISTATUSVH'
@AbapCatalog.compiler.compareFilter: true
@AbapCatalog.preserveKey: true
@AccessControl.authorizationCheck: #NOT_REQUIRED
@EndUserText.label: 'Value Help for Status'
@Search.searchable: true
@ObjectModel.representativeKey: 'DomainKey'
@ObjectModel.resultSet.sizeCategory: #XS //ドロップダウンにする
define view Z_I_StatusVH as select from dd07l as Domain
association [0..*] to Z_I_Domain_Text as _Text
on $projection.DomainName = _Text.DomainName
and $projection.DomainKey = _Text.DomainKey
{
@UI.hidden: true
key Domain.domname as DomainName,
@ObjectModel.text.association: '_Text'
@Search.defaultSearchElement: true
key Domain.domvalue_l as DomainKey,
_Text
}
where Domain.as4local = 'A'
and Domain.domname = 'D_SO_OR'
Consumption View
④と同様に、検索ヘルプ用のアノテーションをつけます。
define view Z_C_SO_MOB67
as select from Z_I_SO_MOB67
association [0..*] to Z_C_SOI_MOB67 as _Item on $projection.UUID = _Item.ParentUUID
association [0..1] to Z_C_BPA_MOB67 as _Customer on $projection.BpId = _Customer.BpId
association [0..*] to Z_I_StatusVH as _StatusVH on $projection.DeliveryStatus = _StatusVH.DomainKey //検索ヘルプ用のAssociation
{
//Z_I_SO_MOB67
key node_key as UUID,
@Search.defaultSearchElement: true
so_id as SoId,
...
@Consumption.valueHelp: '_StatusVH' //検索ヘルプを設定
delivery_status as DeliveryStatus,
...
_StatusVH //Associationを公開
####メタデータ
DeliveryStatusにsap:value-list="fixed-values"
というアノテーションがついています。これによってドロップダウンとして表示されます。
<Property Name="DeliveryStatus" Type="Edm.String" MaxLength="1" sap:display-format="UpperCase" sap:label="Delivery Status"
sap:quickinfo="EPM: Sales Order Ordering Status" sap:value-list="fixed-values"/>
検索ヘルプとしてのアノテーションは④と変わりません。
<Annotations Target="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67Type/DeliveryStatus" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<Annotation Term="Common.ValueList">
<Record><PropertyValue Property="Label" String="Value Help for Status"/><PropertyValue Property="CollectionPath" String="Z_I_StatusVH"/><PropertyValue Property="SearchSupported" Bool="true"/>
<PropertyValue Property="Parameters">
<Collection>
<Record Type="Common.ValueListParameterInOut"><PropertyValue Property="LocalDataProperty" PropertyPath="DeliveryStatus"/><PropertyValue Property="ValueListProperty" String="DomainKey"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="DomainName"/></Record>
<Record Type="Common.ValueListParameterDisplayOnly"><PropertyValue Property="ValueListProperty" String="DomainKey_Text"/></Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
</Annotations>
###⑥アクション
Delivery Statusを更新するアクションを追加します。ここではボタンを表示するためのアノテーションについて説明します。アクションの実装についてはこちらをご参照ください。
####Metadata Extension
以下を追加します。アクションはlineItemの一種として追加し、#type: FOR_ACTION
を選択します。
@UI: {
selectionField: [{position: 10 }],
lineItem: [
{ position: 10, importance: #HIGH },
{ type: #FOR_ACTION, position: 10, dataAction: 'BOPF:SET_TO_DELIVERED', label: 'Set to Delivered' } //アクションのためのアノテーション
]
}
SoId;
####アノテーションモデル
アノテーションモデルには以下のように反映されます。CDSではSoIdにアノテーションをつけているように見えますが、アノテーションモデルを見るとSoIdとは関係ないことがわかります。LineItemの中に追加する必要から、上のような形を取ったのだと思われます。
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="SoId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataFieldForAction"><PropertyValue Property="Label" String="Set to Delivered"/><PropertyValue Property="Action" String="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67_CDS_Entities/Z_C_SO_MOB67Set_to_delivered"/><PropertyValue Property="InvocationGrouping" EnumMember="UI.OperationGroupingType/Isolated"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedAt"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedBy"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="BpId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="GrossAmount"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="DeliveryStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="OverallStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
</Collection>
</Annotation>
###⑦他アプリへのナビゲーション
Customerのリンクをクリックしたときに、別のアプリにナビゲーションするようにします。
※Detail画面に直接遷移させるためには、以下の条件を満たす必要があります
・ナビゲーション元とナビゲーション先で項目名(今回はBpId)が一致していること
・項目がナビゲーション先のエンティティのキーであること
####Metadata Extension
ナビゲーション元となる項目に以下のアノテーションをつけます。
・lineItemにtype: #WITH_INTENT_BASED_NAVIGATION
とsemanticObjectAction
を指定
・@Consumption.semanticObject
を指定
この2つでナビゲーション先アプリのセマンティックオブジェクト + アクションを指定します。
@UI: {
selectionField: [{position: 30 }],
lineItem: [
{ position: 40, importance: #HIGH, type: #WITH_INTENT_BASED_NAVIGATION, semanticObjectAction: 'manage' }
]
}
@Consumption.semanticObject: 'BusinessPartner'
BpId;
####アノテーションモデル
アノテーションモデルには以下のように反映されます。
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="SoId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataFieldForAction"><PropertyValue Property="Label" String="Set to Delivered"/><PropertyValue Property="Action" String="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67_CDS_Entities/Z_C_SO_MOB67Set_to_delivered"/><PropertyValue Property="InvocationGrouping" EnumMember="UI.OperationGroupingType/Isolated"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedAt"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedBy"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataFieldWithIntentBasedNavigation"><PropertyValue Property="SemanticObject" String="BusinessPartner"/><PropertyValue Property="Action" String="manage"/><PropertyValue Property="Value" Path="BpId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="GrossAmount"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="DeliveryStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="OverallStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
</Collection>
</Annotation>
WebIDEでナビゲーションのテストをする方法については、以下のブログをご参照ください。
Fiori App To App Navigation in Web IDE Full-Stack
##全体
今回の実装にかかわるアノテーションの全体は以下のようになります。
###Metadata Extension
@Metadata.layer: #CORE
@UI: {
headerInfo: {
typeNamePlural: 'Sales Orders'
}
}
annotate view Z_C_SO_MOB67
with
{
@UI: {
selectionField: [{position: 10 }],
lineItem: [
{ position: 10, importance: #HIGH },
{ type: #FOR_ACTION, position: 10, dataAction: 'BOPF:SET_TO_DELIVERED', label: 'Set to Delivered' }
]
}
SoId;
@UI: {
selectionField: [{position: 20 }],
lineItem: [{ position: 20, importance: #HIGH }]
}
CreatedAt;
@UI: {
lineItem: [{ position: 30, importance: #HIGH }]
}
CreatedBy;
@UI: {
selectionField: [{position: 30 }],
lineItem: [
{ position: 40, importance: #HIGH, type: #WITH_INTENT_BASED_NAVIGATION, semanticObjectAction: 'manage' }
]
}
@Consumption.semanticObject: 'BusinessPartner'
BpId;
@UI: {
selectionField: [{ position: 40 }]
}
Currency;
@UI: {
lineItem: [{ position: 50, importance: #MEDIUM }]
}
GrossAmount;
@UI: {
selectionField: [{ position: 50 }],
lineItem: [{ position: 60, importance: #MEDIUM }]
}
DeliveryStatus;
@UI: {
lineItem: [{ position: 70, importance: #MEDIUM }]
}
OverallStatus;
}
####アノテーションモデル
<Annotations Target="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67Type">
<Annotation Term="UI.HeaderInfo">
<Record><PropertyValue Property="TypeName" String="Sales Order Header"/><PropertyValue Property="TypeNamePlural" String="Sales Orders"/></Record>
</Annotation>
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="SoId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataFieldForAction"><PropertyValue Property="Label" String="Set to Delivered"/><PropertyValue Property="Action" String="Z_C_SO_MOB67_CDS.Z_C_SO_MOB67_CDS_Entities/Z_C_SO_MOB67Set_to_delivered"/><PropertyValue Property="InvocationGrouping" EnumMember="UI.OperationGroupingType/Isolated"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedAt"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="CreatedBy"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataFieldWithIntentBasedNavigation"><PropertyValue Property="SemanticObject" String="BusinessPartner"/><PropertyValue Property="Action" String="manage"/><PropertyValue Property="Value" Path="BpId"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/High"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="GrossAmount"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="DeliveryStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
<Record Type="UI.DataField"><PropertyValue Property="Value" Path="OverallStatus"/><Annotation Term="UI.Importance" EnumMember="UI.ImportanceType/Medium"/></Record>
</Collection>
</Annotation>
<Annotation Term="UI.SelectionFields">
<Collection>
<PropertyPath>SoId</PropertyPath>
<PropertyPath>CreatedAt</PropertyPath>
<PropertyPath>BpId</PropertyPath>
<PropertyPath>Currency</PropertyPath>
<PropertyPath>DeliveryStatus</PropertyPath>
</Collection>
</Annotation>
</Annotations>
##トラブルシューティング
このアプリを作るにあたり遭遇した問題について、以下の記事に書いています。
【CDS】UIアノテーションが更新されないときは
【BOPF】明細が登録できないときは