【BOPF】Actionの実装

##はじめに
CDS + BOPFでFioriアプリケーションを作るシリーズの続きです。
今回はActionを使って選択したデータをコピーする機能を追加します。

前回の記事
[【BOPF】Determination, Validationの実装](https://qiita.com/tami/items/71a80205d88893c69163)

##今回のゴール
- UIにコピーボタンが表示されるようにする
- コピーボタンを押すと、選択した行のデータがコピーされる

##ステップ
1. BOPFにActionを追加
2. UIアノテーションを追加

##BOPFにActionを追加
###Actionを登録する
[前回](https://qiita.com/tami/items/71a80205d88893c69163)と同じ要領で、BOPFのルートノードにアクションを追加します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/b784b8a0-1f22-9c74-13ea-7f54f38a58b0.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/9d08ad29-cd3b-fd1f-6476-869ce50870f1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2bde8eb6-bf08-2908-c070-617f810e0776.png)
ここで有効化します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/1e6ad689-06cf-77fc-93f0-eb666211e01a.png)

いまさらですが、機能ごとに別のクラスになるので、同じBOPFに属するクラスを識別できる命名にしたほうがいいですね。

###生成されたクラスの構造
/BOBF/CL_LIB_A_SUPERCL_SIMPLEというクラスを継承しています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/57920ee8-3520-4118-2a61-403926374eca.png)
/BOBF/IF_FRW_ACTIONというインターフェースが実装されています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/26902044-a16d-6abc-25e0-636473edd56c.png)
Determination, Validationと同様にメソッドは3つあり、EXECUTEというメソッドを再定義するようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/e4992b79-454d-e192-657a-5e7b970dd2bc.png)

###Actionロジックを実装する
今回はコピー機能を作るので、以下のステップで実装します。
1. 処理対象のデータ（選択された行）を取得する
2. 選択された行をコピーし、新しいデータを登録する

####処理対象のデータ（選択された行）を取得する
Determinationと同様の方法で処理対象のデータを取得します。

```abap
    DATA: lt_data TYPE ztmob58_c_students.

    io_read->retrieve(
      EXPORTING
        iv_node                 = is_ctx-node_key        " Node Name
        it_key                  = it_key                 " Key Table
      IMPORTING
        et_data                 = lt_data                " Data Return Structure
    ).
```

####選択された行をコピーし、新しいデータを登録する
まず、2つの変数を追加します。

```abap
    DATA: lt_data TYPE ztmob58_c_students,
          ls_copy TYPE zsmob58_c_students,
          lo_copy TYPE REF TO data.
```
処理対象行をコピーし、createメソッドに渡します。

```abap
    LOOP AT lt_data INTO DATA(lw_data).
      "行をコピー
      ls_copy = VALUE #(
                         first_name = | { lw_data-first_name } _copy |  "コピーしたことがわかるように、末尾に_copyとつける
                         last_name  = lw_data-last_name
                         birthday   = lw_data-birthday
                         region     = lw_data-region
                         city       = lw_data-city
                         street     = lw_data-street
                         location   = lw_data-location
                        ).

      "データオブジェクトを登録      
      CREATE DATA lo_copy TYPE zsmob58_c_students.
      "データオブジェクトにコピーした行を参照させる
      ASSIGN lo_copy->* TO FIELD-SYMBOL(<fs_copy>).
      IF <fs_copy> IS ASSIGNED.
        <fs_copy> = ls_copy.
      ENDIF.

      io_modify->create(
        EXPORTING
          iv_node            = is_ctx-node_key   " Node to Create
          is_data            = lo_copy           " Data
        IMPORTING
          ev_key             = DATA(lv_key)
      ).

    ENDLOOP.
```

データオブジェクトやフィールドシンボルが出てきて複雑ですが、次のようなことをしています。
①処理対象行をコピーして、構造ls_copyに格納
②データオブジェクトを登録（createメソッドの引数is_dataがオブジェクト型なので、オブジェクト型を渡すため）
③データオブジェクトにls_copyの内容を参照させる（フィールドシンボルを経由して）

参考にしたのは以下の記事です。
https://eprs.online/2019/04/06/e2e-app-bopf-development/#action

##UIアノテーションを追加
画面にCOPYボタンを表示させるために、以下のUIアノテーションを追加します。
`@UI.lineItem: [{type:#FOR_ACTION, dataAction: 'BOPF:<ACTIONの名称>', label:'ボタンのテキスト'}]`

Metadata Extensionは全体で以下のようになります。

```abap
@Metadata.layer: #CORE
annotate view ZMOB58_C_STUDENTS
    with 
{
    @UI.selectionField: [{position: 10 }]
    @UI.lineItem: [{position: 10 }]
    @UI.identification: [{position: 10, label: 'First Name'}]
    first_name; 
    @UI.selectionField: [{position: 20 }]
    @UI.lineItem: [{position: 20 }]
    @UI.identification: [{position: 20, label: 'Last Name'}]
    last_name; 
    @UI.lineItem: [{position: 30 }]
    @UI.identification: [{position: 30, label: 'Birthday'}]
    birthday; 
    @UI.selectionField: [{position: 30 }]
    @UI.lineItem: [{position: 40}]
    @UI.identification: [{position: 40, label: 'Region'}]
    region; 
    @UI.lineItem: [{position: 50, type:#FOR_ACTION, dataAction: 'BOPF:COPY', label:'Copy'}]
    @UI.identification: [{position: 50, label: 'City'}]
    city; 
    @UI.identification: [{position: 60, label: 'Street'}]
    street; 
    @UI.identification: [{position: 70, label: 'Location'}]
    location;
}
```

###問題
今回、cityという項目に対してactionを追加しましたが、どの項目につけても動作は変わりません。（行に対するアクションなので）
ただし、actionを付与した項目は一覧上に表示されなくなる、という挙動をします。解決方法については調査できていません。
暫定策として、表示されなくても困らない項目にactionを設定しました。

2020/12/8更新：以下のように一つの項目に複数のlineItemアノテーションをつけることができます。こうすると、その項目は一覧にも表示されます。

```abap
    @UI.lineItem: [
        { position: 10 },
        { position: 10, type:#FOR_ACTION, dataAction: 'BOPF:COPY', label:'Copy'}]
    first_name;
```


##実行結果
Copyボタンを押すと、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/2ef3bb9a-6e57-286b-776e-4e112c061fed.png)
何も起きません。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/1ed3c554-0e13-c256-cc92-20796e79b19b.png)
Goボタンを押すとコピーされた行が表示されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/276021/d5085106-6015-9fd8-2f18-4749537aa2c1.png)

アクション後のリフレッシュや、メッセージを出すなどの機能については研究が必要そうです。とりあえず、アクションが動作していることは確認できました。