大量のコンテンツがあるScreenで、特定の箇所を共有したい(URLを開くと、その箇所へスクロールさせたい)。
環境情報
ODC Studio(Version 1.5.9)
先に結論
以下の実装が良さそう。
- Screenにスクロール先WidgetのIdを渡すInput Parameterを追加
- ScreenのOnReadyで以下の処理
- 渡されたWidgetのIdが許容される値かチェックする
- 許容される値であれば、OutSystemsUI/ScrollToElement ActionのWidgetIdパラメータに渡す
一般のWebページならURLにFragmentを付与すれば良い
URI フラグメント - URI | MDNより、FragmentはURL内の#で始まる部分。
フラグメントは URI の最後の部分で、# の文字で始まる部分です。文書内のセクションや動画内の位置など、リソースの特定の部分を識別するために使用されます。
HTMLでページを開いた時、特定の場所にスクロール表示するには、URL末尾に「#要素のid」を追加する。
HTML 文書内の場合は、要素の id 属性となり、ブラウザーはその要素までスクロールします。
OutSystemsアプリケーションでは、この方法はうまくいかない。
以下、この点について確認し、解決策を検討する。
検証用Screenの実装
Sectionを3つ配置し、Nameプロパティを、それぞれSection1, Section2, Section3とした。
URLにFragmentを付与するだけだとうまくいかない
画面上のSection1にスクロールした状態で開きたいとき、以下のアドレスにアクセスする。
「https://<FQDN>/<App名>/<Screen名>#Section1」
一般のWebページならこれでうまくいくのだが、OutSystemsではうまくいかない。
恐らくReactの影響かと思われる(OutSystemsは、内部でReactを使っている)。
- ReactとFragmentをキーワードにして検索すると、同じ問題にぶつかっている投稿がいくつかある
- SPAであるから、サーバーから返されるHTMLは枠だけが記述されていて、後からJavaScriptでページ構築される? → 取得時には遷移先となる場所がない
OutSystemsUI/ScrollToElement ActionでFragmentまでスクロール → 可能だが問題あり
JavaScriptでFragmentを取得
現在のURL内のFragmentはJavaScriptを使えば取得できる。
window.location.hashを使う。
Location: hash property - Web APIs | MDN
JavaScript要素をAction Flowに配置し、Output ParameterとしてFragmentを追加。
コード例。
window.location.hashは#で始まるが、Fragmentがないときは空文字列を返す。値が入っているか確認し、入っているときは、先頭の1文字を削って返している。
$parameters.Fragment = window.location.hash;
if ($parameters.Fragment.startsWith('#'))
$parameters.Fragment = $parameters.Fragment.substring(1);
ScreenのOnReadyでScrollさせる
ScreenのOnReady Eventを選ぶのは、必要なDOMの準備が終わったタイミングで、1回の画面表示につき1回だけ発生するEventであるため。
OnReadyのハンドラー例。
JavaScriptコードで取得したFragmentをWidgetIdパラメータに設定してScrollToElement Actionを呼ぶ。
補足
- ListAppendAllでは、画面上でスクロール先として使えるWidgetのIdを、Local VariableのListに登録している
- GetFragmentの実装は、↑に示したJavaScript
- ListIndexOfで取得したFragmentが使えるIdと合致するかチェックしている(これは明示的に許可した値だけを利用可能にすることで、セキュリティに想定外の問題が起きるのを防ぐため)
- 許容されるFragmentであれば、ScrollToElementにGetFrgmentの結果を渡してスクロールさせる
結果
ブラウザのアドレスバーにFragment付きのURLを入力して開いたところ、想定通り、指定した要素までスクロールして開いた。
しかし、以下のパターンの操作をすると、画面が全面白の状態のまま表示が止まってしまう(通信によってページのHTML自体は取得できている)。
- 一度ページを開く
- アドレスバーのURLを編集し、Fragmentを別の値に変更する
- アドレスバーでEnterキー
これもReactが原因の気がする。
TODO: Reactを後で調べる
FragmentでなくScreenのInput Parameterでスクロール先を指定する
基本的な方法は↑と同じだが、Fragmentではなく、Screenに追加したInput Parameterでスクロール先を指定する(↓の例では、TargetInThePage)。
OnReadyの実装は基本的に同じで、GetFragment呼び出しを無くし、そのOutput Parameterの代わりにTargetInThePageを使うだけ。
この方法なら、アドレスバーでパラメータを変更→Enterキーの操作でも問題なく再表示できた。