はじめに
メールやブラウザを使用した既存システムをTeamsアプリに置き換える場合、ブラウザの部分を置き換える手段として、タスクモジュールがよく使用されます。
その理由は、SDK固有の簡単な記述を挿入するだけで、既存のブラウザベースのUIをそのまま使用できるからです。
タスクモジュールを使用すると、ブラウザと同様、HTMLとJavaScriptで実装されたWeb画面をUIとして提供できます。
タスクモジュールは、Chromiumベースのレンダリングエンジンを搭載したモーダルポップアップです。
したがって、追加で必要となるのは、「呼び出し」「結果の受け取り」および「画面遷移」にあたる箇所にSDK固有の簡単な記述を挿入することだけです。
表示内容については、ブラウザ向けのWeb開発フレームワークをそのまま使用できます。
たとえば、タスクモジュールを使用すると、「情報の入力・編集」の後に、「申請」「承認・却下」「計算・清算」などを行うといった、ブラウザで実現されていたのと同じUIを、Teamsから離れることなく実現できます。
また、以下の理由からも、数あるUIの選択肢のうち、「タスクモジュール」が多用されています。
- タブにおいて画面変化させる場合、SPAとしての実装に限られ、別URLへの画面遷移ができない
- ボットにおいてアダプティブカードを更新する場合、全体を書き換える形になる。また、Ajaxによる非同期処理(入力値の対話的な検証、インクリメンタルサーチ、等)ができない
- Teamsからいったん離れてブラウザを開く場合、「Teams内でシナリオを完結させる」「Teamsからいったん離れる場合はユーザーにそのことを明示して許可を得る」というストア公開の審査ガイドラインに抵触してしまう
本稿では、2つのコードサンプルを参照しながら、タスクモジュールの「呼び出し」や「結果の受け取り」ならびに「画面遷移」に関する、上記のSDK固有の簡単な記述箇所について要点をまとめてみます。
呼び出し
タスクモジュールを呼び出す方法には、次の3つがあります。
- タブやタスクモジュールのボタンなどからTeams JavaScript SDKのstartTaskメソッドを呼び出す
- アダプティブカードのカードアクションとしてtask/fetchアクションを発行する
- メッセージ拡張のアイコン、チャット画面の入力欄、タブやタスクモジュールのボタンなどの要素、およびアダプティブカードのカードアクションからディープリンクを張る
タスクモジュールを呼び出す際には、タスクモジュールを表現するデータとして、以下のプロパティを含むJSONデータ(ディープリンクの場合はクエリ文字列)を渡します。
このデータは、「タスクインフォ」(TaskInfo)、または「タスクモジュールインフォ」(TaskModuleInfo)と呼ばれます。
- title(タイトル): アプリ名の下、アプリアイコンの右横に表示される文字列
- height(高さ)とwidth(幅): ピクセル数ないしは”small””medium””large”を指定する
- url(WebページのURL): 表示内容となるWebページのURL
(注意: iFrameに埋め込むURLではない) - card(アダプティブカード): 表示内容がアダプティブカードの場合はurlではなくアダプティブカードのJSONデータを指定する
(注意: urlとcardはいずれかを指定する。両方を指定してはいけない) - fallbackUrl(フォールバックURL): クライアントがタスクモジュールをサポートしていない場合に、代わりにブラウザに表示させるURL
タスクモジュールは、下図のような要素から構成されます。このうち、titleプロパティにより「③タイトル」の文字列、heightおよびwidthプロパティによりタスクモジュール全体のサイズ、urlまたはcardプロパティにより「⑤⑥表示エリア」に表示される内容が指定されます。
Submitボタンは、ユーザーが入力した値など、タスクモジュールの結果を呼び出し側に戻す必要がある場合に実装します。
Submitボタンの実装は、呼び出される側のタスクモジュールのコード内で行います(詳しくは後述します)。情報を表示するだけの場合は、Submitボタンの実装は必要ありません。
表示エリアに表示する内容としては、HTMLによる本格的なWebページの実装に加えて、iFrameを介して既存のWebページを表示させるだけの方法や、アダプティブカードを指定するという簡便な方法が利用できます(それぞれの方法について実装例を後述します)。
アダプティブカードを指定する場合には、上記のように、urlプロパティではなくcardプロパティを指定します。
アダプティブカードの場合、HTMLよりも表現は制約されますが、統一されたデザインを容易に実現できるというメリットがあります。
以下では、タスクモジュールを呼び出す典型的な例として、「タブから呼び出す」(startTaskメソッドを呼び出す)場合と、「ボットから呼び出す」(task/fetchアクションとして呼び出す)場合について、コードサンプル1の例を参照しながら見ていきます。
タブから呼び出す
startTaskメソッドを使用してタスクモジュールを呼び出す際には、第1パラメータのタスクインフォに加えて、第2パラメータとして「サブミットハンドラ」と呼ばれるコールバック関数を渡します。
サブミットハンドラには、第1引数としてエラーを表す文字列、第2引数として結果を表すJSONオブジェクト(Webページの場合は文字列でもよい)が渡されるとして定義します。
submitHandler(error: string, result: string | any)これにより、呼び出しの最後に、呼び出し元がコールバックされ、タブ上でエラーと結果を処理するためのロジックが実行されます。
基本としては、サブミットハンドラを指定する
サブミットハンドラは(すなわちSubmitボタンは)省略可能です。しかし、タスクモジュールで発生したエラーに対処したり、タスクモジュールから戻された情報を利用するために、指定することが基本になります。
たとえば、コードサンプル1のファイル「selector.html」で定義されているYouTubeビデオのIDを選択するためのタスクモジュールの場合、呼び出し側のイベントハンドラであるonChangeVideo([Change Video ID]ボタン押下時に発火)において、submitHandlerという名前のコールバック関数が定義されて(下図②)、startTaskメソッドに渡されています(下図③)。
このsubmitHandler関数の中では、結果(result)を引数として、setYouTubeVideoIdという関数が実行されるように定義されています(下図②)。
これにより、startTaskメソッドの呼び出し先である「selector.html」(下図⑥のタイトル「YouTube Video Selector」のタスクモジュール)のSubmitボタン( [Update]ボタン)押下時に、このsubmitHandler関数がコールバックされ、その際に、このsetYouTubeVideoIdが実行されることになり、それにより、タブ上のYouTube Video IDという項目の値が「kaIV4dG-oFo」に変更されます(下図⑦)。
上図において、全体の動作を最初から辿っていきますと、まず、urlプロパティ(①)を含むタスクインフォ(taskModuleInfo)が、サブミットハンドラ(②のsubmitHandler)とともにstartTaskメソッドに渡されています(③)。
onChangeVideoイベントハンドラ(④)が、タブ上の [Change Video ID] ボタン(⑤)の押下によって起動されると、このstartTaskメソッドが実行され、タスクモジュールが表示されます(⑥)。
次に、タスクモジュール上でユーザーがビデオIDを変更して [Update] ボタン(タスクモジュールのSubmitボタン)をクリックすると、タスクモジュールが閉じられます。
その際に、Submitボタン押下に起因する処理(後述のsubmitTaskメソッド)により結果(result)が渡され、その結果を引数とするコールバック関数(上記のサブミットハンドラ)が元のタブ上で実行され(つまり、呼び出し元がコールバックされ)、それにより、元のタブ上のビデオIDが変更されます(⑦)。
なお、サブミットハンドラに渡されるエラーとしては、以下のものがあります。実行時に該当する状態であれば、結果はnullのままエラーだけが戻されます。
- card、urlとも指定していないか、または両方とも指定してしまっている
- appID が正しくない
- ユーザーがタスクモジュールをキャンセルしたまたは閉じた
例外的に、サブミットハンドラを省略できる場合がある
情報を表示するだけのタスクモジュールの場合など、サブミットハンドラを(すなわちSubmitボタンを)省略できる場合があります。
情報を表示するだけのタスクモジュールの場合でも、タスクモジュール側で発生したエラーの検知が必要な場合は、サブミットハンドラを実装することになります。
たとえば、コードサンプル1のファイル「player.html」で定義されているYouTubeビデオを再生するためのタスクモジュールの場合、呼び出し側のイベントハンドラであるonShowVideo([Show Video]ボタン押下時に発火)においては、サブミットハンドラが省略されています(下図⑦⑧)。
上図において、全体の動作を最初から辿っていきますと、まず、Reactフレームワークにおいて、<Input>コンポーネントの利用が可能となっています(①②)。
また、youTubeVideoIdという変数がstate変数として定義されています(③)。
これにより、タブ上のYouTube Video IDアイテムの値が状態(state)として制御されます(④⑤)。
このyouTubeVideoIdの値が、プレースホルダに代入されて、YouTubeビデオのURLが作成され、タスクインフォ(taskModuleInfo)のurlプロパティとして設定されます(⑥)。
また、このタスクインフォがstartTaskメソッドの第1パラメータ(唯一のパラメータ)として渡されます(⑦)。
そして、[Show Video] ボタンの押下時に(⑨)、onStartVideoイベントハンドラが起動され(⑧)、このstartTaskメソッドが実行されます。
その結果、上記のYouTubeビデオを再生するためのタスクモジュールが表示されます(⑩)。
このとき、表示されるタスクモジュールにはSubmitボタンがなく、また、サブミットハンドラも渡されていないので、タスクモジュールを閉じたときに、エラーや結果に対するロジックは実行されません。
「player.html」はiFrame埋め込みの例になっている
コードサンプル1のファイル「player.html」で定義されているYouTubeビデオを再生するためのタスクモジュールは、iFrameによる既存Webページの埋め込みの例になっています。
すなわち、下図において、URLをパースして「vid」というパラメータの値をビデオIDとして抽出し(①)、YouTubeビデオのURLプレフィックス「https://www.youtube.com/embed/」と文字列結合したURLを作成して(②)、iframe要素のsrcプロパティとして設定することにより(③)、ビデオIDで指定されるYouTubeビデオを再生するWebページをiFrameに埋め込んで表示させています(④⑤)。
前述のとおりこのタスクモジュールにはSubmitボタンが設定されていないため、タスクモジュールの利用終了時には、[x]ボタン(Closeボタン)をクリックして閉じることになります。
iFrameではなくWebページを直書きする場合は@PreventIframeしておく
さらに、参考のため、iFrameではなくWebページを直書きする例として、コードサンプル1のファイル「selector.html」で定義されているYouTubeビデオのIDを選択するためのタスクモジュールのHTMLコードも示しておきます(①)。
ここで注目できるのは、サーバー側のtsファイルに@PreventIframeデコレータを記述している点です(②)。これにより、外部からiFrameを使用してページが不正使用されないようにしています。
express-msteams-hostライブラリの@PreventIframeデコレータについては、express-msteams-host/README.md · GitHub、Create a personal tab – Teams | Microsoft Docsなどのドキュメントに説明があります。
なお、このタスクモジュールのレンダリングは、tsxファイルを参照する形で定義されています(③④⑤)。
呼び出し先のtsxファイルでは、<Input>コンポーネントの値が変更された(onChangeイベント)場合にhandleOnChangedというイベントハンドラが(⑥⑦)、<Button>コンポーネントがクリックされた(onClickイベント)場合にhandleOnClickというイベントハンドラが(⑧⑨)、それぞれ反応するように定義されています。
ボットから呼び出す
結果の受け取り
画面遷移(チェーン、連結)
まとめ
エラーが表示
コメント