LoginSignup
2
0

More than 3 years have passed since last update.

【SAPUI5】onExitで何をすればいいのか

Last updated at Posted at 2020-02-08

はじめに

コントローラーには4つのライフサイクルメソッドがあります。

  • onInit
  • onBeforeRendering
  • onAfterRendering
  • onExit

以下のブログで詳しく説明されています。
https://blogs.sap.com/2018/11/12/sapui5-controller-lifecycle-methods-explained/
このうち、onExitについては以下のように書かれていました。

This method is called upon destruction of the View. The controller should perform its internal destruction in this hook. It is only called once per View instance, unlike the onBeforeRendering and onAfterRendering hooks.

ビューが削除されるときに呼ばれるフックで、コントローラーの内部的な削除をこの中で行うということです。

疑問

私には以下の疑問がありました。
1. WebIDEで実行したときに、onExitを入れても呼ばれなかった。実際いつ呼ばれるのか
2. onExitの中で、具体的に何をする必要があるのか。しなかったらどうなってしまうのか

onExitと似た役割のものとして、Component.jsにdestroyというメソッドがあります。これの用途についても同じ疑問がありました。

1つ目の疑問については、SAP Communtyで検索したところ、アプリを抜けてラウンチパッドに戻るときに呼ばれることがわかりました。確かに、WebIDEでflpSandbox.htmlから実行した場合には呼ばれました(index.htmlで実行すると呼ばれない)。

質問した結果

2つ目の疑問について質問した結果、以下の回答をいただきました。

onExit, destroyで何をするか

  • onExitやdestroyは、ビューにバインドされていないコントロールを削除するときに使う
  • 回答者の方は、主にカスタムライブラリで使用している
  • ビューにバインドされていればビューと一緒に自動で削除されるが、バインドされていない場合はマニュアルで消す必要がある

それをしないと、どんな悪影響があるか

  • コントロールのidをハードコーディングしている場合、Launchpadからアプリに戻ったときにidの重複が発生する可能性がある

すなわち、コントロールのidがハードコードされていて、かつビューにバインドされていない場合、onExitやdestroyでコントロールを削除する必要があるということです。

image.png

動作確認

以下のパターンで動作確認をしてみました。

  1. onExitの検証
    1. idの重複が発生するパターン
    2. idの重複が発生しないパターン
      1. ビューにバインドする
      2. 任意のタイミングで削除する
      3. onExitで削除する
  2. destroyの検証
    1. idの重複が発生するパターン
    2. idの重複が発生しないパターン
      1. destroyで削除する

1. onExitの検証

1-1. idの重複が発生するパターン

ダイアログ用のフラグメントを作成します。Dialogコントロールにはidをハードコーディングしています。

<core:FragmentDefinition
    xmlns="sap.m"
    xmlns:core="sap.ui.core">

    <Dialog
        id="dialog123"
        title="Sample Dialog">
        <content>
            <Text text="This is a sample Dialog" />
        </content>
        <beginButton>
            <Button type="Emphasized" text="OK" press="onClose"/>
        </beginButton>
    </Dialog>   
</core:FragmentDefinition>

ビューにバインドせずにダイアログを生成します。

        onDialogOpen: function () {
            if(!this.oDialog) {
                this.oDialog = sap.ui.xmlfragment("demo.Train_24_onExit.fragment.Dialog", this);
                //this.getView().addDependent(this.oDialog); ビューにバインドしない
            }
            this.oDialog.open();
        },

        onClose: function () {
            this.oDialog.close();
        }

ラウンチパッドから起動します。(WebIDEのflpSandbox.htmlで実行)

image.png

ダイアログを出します。
image.png

ラウンチパッドに戻り、もう一度実行するとid重複でエラーになります。このときDOMを確認しましたが、同じidの要素はありませんでした。見えないところで持っているのかもしれません・・・
image.png

1-2. idの重複が発生しないパターン

以下、いずれの方法でもidの重複が発生しなくなります。

1-2-1. ビューにバインドする

これが一般的な方法かと思います。

        onDialogOpen: function () {
            if(!this.oDialog) {
                this.oDialog = sap.ui.xmlfragment("demo.Train_24_onExit.fragment.Dialog", this);
                //ビューにバインド
                this.getView().addDependent(this.oDialog);
            }
            this.oDialog.open();
        },
1-2-2. 任意のタイミングで削除する

ここでは、ダイアログを閉じたときにダイアログを消しています。

        onClose: function () {
            //this.oDialog.close();
            this.oDialog.destroy();
        }
1-2-3. onExitで削除する
        onExit: function () {
            this.oDialog.destroy();
        }

2. destroyの検証

2-1. idの重複が発生するパターン

カスタムライブラリを用意します。ボタンを表示するだけのシンプルな機能です。ボタンのidはハードコーディングしています。

Component.js
sap.ui.define([
    "sap/ui/core/UIComponent",
    "sap/m/Button",
    "sap/m/MessageBox"
], function (UIComponent, Button, MessageBox) {
    "use strict";

    return UIComponent.extend("demo.library.zdemolibrary.myButton.Component", {

        metadata: {
            manifest: "json"
        },

        init: function () {
            UIComponent.prototype.init.apply(this, arguments);
        },

        destroy: function () {
            //UIComponent.prototype.destroy.apply(this, arguments);
        },

        createContent: function  () {
            var oButton = this._getButton();
            return oButton;
        },

        onPress: function () {
            MessageBox.alert("Component Button pressed!");
        },

        _getButton: function () {
            if (!this.oButton) {
                this._oButton = new Button("button123", { //idをハードコード
                    text: "myButton",
                    type: "Emphasized",
                    press: this.onPress
                });
            }
            return this._oButton;
        }
    });
});

これをアプリに組み込みます。

manifest.json
        "dependencies": {
            "minUI5Version": "1.65.6",
            "libs": {
                "sap.ui.layout": {},
                "sap.ui.core": {},
                "sap.m": {},
                "demo.library.zdemolibrary": {
                    "async": true
                }
            },
            "components": {
                "demo.library.zdemolibrary.myButton": {
                    "lazy": true
                }
            }
        },
        "componentUsages": {
            "myButton": {
                "name": "demo.library.zdemolibrary.myButton",
                "settings": {},
                "componentData": {},
                "manifest": true
            }
        }
App.controller.js
        onInit: function () {
            this._loadComponent();
        },

        _loadComponent: function () {
            this.getOwnerComponent().createComponent({
                usage: "myButton",
                settings: {},
                componentData: {},
                async: true
            }).then(function(oComp) {
                this.byId("box").addItem(new ComponentContainer({
                    component: oComp
                }));                         
            }.bind(this)).catch(function(oError) {
                MessageBox.error(oError.message);
            });
        }
App.viel.xml
    <App id="app">
        <pages>
            <Page id="page" title="{i18n>title}">
                <content>
                    <VBox id="box">
                    </VBox>
                </content>
            </Page>
        </pages>
    </App>

ラウンチパッドから起動します。
image.png

ラウンチパッドに戻り、もう一度実行するとid重複でエラーになります。
image.png

2-2. idの重複が発生しないパターン

2-2-1. destroyで削除する

ライブラリのComponent.jsのdestroyで、コンポーネントを削除します。

Component.js
        destroy: function () {
            UIComponent.prototype.destroy.apply(this, arguments);
        }       

まとめ

ビューのコントロールは、コントローラーから操作する都合上、idをつけることが多いと思います。そのときは、ビューにバインドするようにすれば、onExitでは特に何もしなくても大丈夫です。
カスタムライブラリは、ビューにバインドされるとは限らないので、常にdestroyを実行したほうがいいと思いました。

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0