07. チュートリアル: かんばん ボードを作成する
一般的な Reactor ガイドと FRToolbox リファレンス ドキュメントでは、Reactor が提供できる一連の機能について解説しています。ただし、FileMaker で実現できないことは実際には実現していません。
Reactor の本来の力は、ダイナミックライブラリを採用した完全にインタラクティブな JavaScript ユーザーインターフェイスで Reactor の機能を使用するときに発揮されます。
この記事では、Reactor を使用して完全にインタラクティブなかんばんボードを作成する方法について説明します。かんばんボードとは、タスクがさまざまな段階でどのように進行するかを示すために使用される視覚的なプロジェクト管理ツールです。壁に貼るポストイットでも、ソフトウェアで実現することもできます。
最初のステップは、できるだけゴールに近づけるライブラリを見つけることです。なぜ誰かがすでに書いたものを書くのですか? jQuery ライブラリにはまさに必要なものが含まれています。
Sortable
インタラクションには、グリッド内でアイテムを並べ替えることができる「Connect Lists」の例があります。垂直方向に上下に移動することも、水平方向に移動することもできます。
https://jqueryui.com/sortable/#connect-lists
では、作りたいものをまとめましょう。私たちは 3 つの宇宙展を計画しており、さまざまな惑星を訪れています (おそらく未来の超高速宇宙船で)。旅行は水平に表され、惑星をこれらの「旅行」の 1 つにドラッグし、惑星を訪れる順序を変更することもできます。
それでは、BlackBox の構築を始めましょう。
Reactor Core のメイン画面から、[+] ボタンをクリックして開始します。
私たちはゼロから始めています。
BlackBox に名前と説明を付けます。:
JQuery ライブラリを使用しているので、ソース ファイルとして次を追加します。:
https://www.dropbox.com/s/i0bw4rpuokdxa3p/jquery-3.4.1.min.js?dl=0
https://www.dropbox.com/s/jpajabb9a9iee88/jquery-ui.min.js?dl=0
[関数] タブに切り替えて、関数と次のパラメーターを追加します。:
最初の 5 つはすべて FileMaker のフィールドです。:
PlanetIDField: 惑星レコードの一意の ID
PlanetNameField: 惑星の名前を含むフィールド
PlanetDescriptionField: 惑星の説明を含むフィールド
PlanetTripNoField: これは、惑星に対して選択した「トリップ」を表します
PlanetSortNoField: これは、トリップ内の惑星の順序を表します
そして最後はテキストパラメータです:
Columns: 表示する列のカンマ区切りリスト
関数の基本を構築したので、次はテンプレートファイルを構築します。
テンプレートファイルとは?これについては、一般的な Reactor チュートリアルで詳しく読むことができますが、基本的には、始めるために最善を尽くします。
JavaScript ライブラリ (JQuery など) を追加した場合は、これが確実にインポートされます。フィールド名である 5 つのパラメーターがある場合、これらは変数として格納されます。また、これらのフィールド名に基づいてレコードを読み取り、更新、作成、または削除するメソッドを開始することにもなりますが、このための独自のメソッドを作成します。
これらの関数をすべて削除し、新しい HTML ソース ファイルが次のようになっていることを確認します。:
<html> <head> <style> <!–– CSS Goes here ––> </style> </head> <script type='text/javascript' src='FRToolBox.js'></script> <script src="jquery-3.4.1.min.js"></script> <script src="jquery-ui.min.js"></script> <!–– Import other sources here ––> <script> FMLink = 'FM.link'; // This is where variables are set from the given parameters var RecordsObj = new Object; FMTableOccurrenceName = '<?Reactor bbdev_TO( $PlanetIDField ) Reactor?>'; FunctionWhereClause = '<?Reactor bbdev_Relationship( bbdev_TO( $PlanetIDField ) ) Reactor?>'; CreationValues = '<?reactor bbdev_relationshipKeyBuilder ( $FMTableOccurrenceName ) reactor?>'; PlanetIDField = '<?Reactor bbdev_Field( $PlanetIDField ) Reactor?>'; PlanetNameField = '<?Reactor bbdev_Field( $PlanetNameField ) Reactor?>'; PlanetDescriptionField = '<?Reactor bbdev_Field( $PlanetDescriptionField ) Reactor?>'; PlanetTripNoField = '<?Reactor bbdev_Field( $PlanetTripNoField ) Reactor?>'; PlanetSortNoField = '<?Reactor bbdev_Field( $PlanetSortNoField ) Reactor?>'; Columns = '<?Reactor $Columns Reactor?>'; </script> <body> <div> <!–– This is the main HTML for the BlackBox ––> </div> </body> </html>
BlackBox の最初のステップは、UI を起動して実行することです。それが完了したら、FileMaker データをそれに接続します。最初に UI を作成する理由は、FileMaker データをどこかに置くためです。そこから始めたら、暗い無関心な空虚にデータを提供することになります。
では、その JQuery の例に戻り、 view source
をクリックしましょう。:
残念ながら、これは単純なコピー アンド ペーストではなく、3 つの単純なコピー アンド ペーストです。コピーs&ペースト?
1. <style>
タグの間のすべてのコンテンツをコピーし、BB <style>
タグの間に貼り付けます。:
#sortable1, #sortable2 { border: 1px solid #eee; width: 142px; min-height: 20px; list-style-type: none; margin: 0; padding: 5px 0 0 0; float: left; margin-right: 10px; } #sortable1 li, #sortable2 li { margin: 0 5px 5px 5px; padding: 5px; font-size: 1.2em; width: 120px; }
2. $( function()
ブロックをコピーして、BB <script>
ブロックの最後に貼り付けます。:
$( function() { $( "#sortable1, #sortable2" ).sortable({ connectWith: ".connectedSortable" }).disableSelection(); } );
3. 下の方にある HTML をコピーして、BB <body>
タグの間に貼り付けます。
<ul id="sortable1" class="connectedSortable"> <li class="ui-state-default">Item 1</li> <li class="ui-state-default">Item 2</li> <li class="ui-state-default">Item 3</li> <li class="ui-state-default">Item 4</li> <li class="ui-state-default">Item 5</li> </ul> <ul id="sortable2" class="connectedSortable"> <li class="ui-state-highlight">Item 1</li> <li class="ui-state-highlight">Item 2</li> <li class="ui-state-highlight">Item 3</li> <li class="ui-state-highlight">Item 4</li> <li class="ui-state-highlight">Item 5</li> </ul>
HTML ファイルの完全な内容は次のようになります。
<html> <head> <style> #sortable1, #sortable2 { border: 1px solid #eee; width: 142px; min-height: 20px; list-style-type: none; margin: 0; padding: 5px 0 0 0; float: left; margin-right: 10px; } #sortable1 li, #sortable2 li { margin: 0 5px 5px 5px; padding: 5px; font-size: 1.2em; width: 120px; } </style> </head> <script type='text/javascript' src='FRToolBox.js'></script> <script src="jquery-3.4.1.min.js"></script> <script src="jquery-ui.min.js"></script> <script> FMLink = 'FM.link'; FMTableOccurrenceName = '<?Reactor bbdev_TO( $PlanetIDField ) Reactor?>'; FunctionWhereClause = '<?Reactor bbdev_Relationship( bbdev_TO( $PlanetIDField ) ) Reactor?>'; CreationValues = '<?reactor bbdev_relationshipKeyBuilder ( $FMTableOccurrenceName ) reactor?>'; PlanetIDField = '<?Reactor bbdev_Field( $PlanetIDField ) Reactor?>'; PlanetNameField = '<?Reactor bbdev_Field( $PlanetNameField ) Reactor?>'; PlanetDescriptionField = '<?Reactor bbdev_Field( $PlanetDescriptionField ) Reactor?>'; PlanetTripNoField = '<?Reactor bbdev_Field( $PlanetTripNoField ) Reactor?>'; PlanetSortNoField = '<?Reactor bbdev_Field( $PlanetSortNoField ) Reactor?>'; Columns = '<?Reactor $Columns Reactor?>'; $( function() { $( "#sortable1, #sortable2" ).sortable({ connectWith: ".connectedSortable" }).disableSelection(); } ); </script> <body> <ul id="sortable1" class="connectedSortable"> <li class="ui-state-default">Item 1</li> <li class="ui-state-default">Item 2</li> <li class="ui-state-default">Item 3</li> <li class="ui-state-default">Item 4</li> <li class="ui-state-default">Item 5</li> </ul> <ul id="sortable2" class="connectedSortable"> <li class="ui-state-highlight">Item 1</li> <li class="ui-state-highlight">Item 2</li> <li class="ui-state-highlight">Item 3</li> <li class="ui-state-highlight">Item 4</li> <li class="ui-state-highlight">Item 5</li> </ul> </body> </html>
「プレビュー」画面に切り替えて、「コンパイル」ボタンをクリックします。新しい BlackBox が目の前に表示されるはずです。:
次に、かんばんボードに惑星名と惑星の説明を表示します。したがって、各 <li>
要素に 2 行目のテキストが含まれるように HTML を変更します。:
<ul id="sortable1" class="connectedSortable"> <li><div class=name>Item 1</div><div class=desc>The first item</div></li> <li><div class=name>Item 2</div><div class=desc>The second item</div></li> <li><div class=name>Item 3</div><div class=desc>The third item</div></li> <li><div class=name>Item 4</div><div class=desc>The fourth item</div></li> <li><div class=name>Item 5</div><div class=desc>The fifth item</div></li> </ul> <ul id="sortable2" class="connectedSortable"> <li><div class=name>Item 1</div><div class=desc>The first item</div></li> <li><div class=name>Item 2</div><div class=desc>The second item</div></li> <li><div class=name>Item 3</div><div class=desc>The third item</div></li> <li><div class=name>Item 4</div><div class=desc>The fourth item</div></li> <li><div class=name>Item 5</div><div class=desc>The fifth item</div></li> </ul>
いいですね。しかし、それはかなり「ポップ」ではありませんか? CSS ( <style>
ブロックの内容) を次のように置き換えます。:
#sortable1, #sortable2 { width: 142px; min-height: 20px; list-style-type: none; margin: 0; padding: 5px 0 0 0; float: left; margin-right: 10px; } #sortable1 li, #sortable2 li { margin: 0 5px 5px 5px; padding: 5px; width: 120px; border:black solid 1px; background-color:#123a53; color:white; font-size:16px; }
再コンパイルして見てみましょう:
ほら、少し良くなっています。さらにCSSを自由に調整してください。
OK、ベース UI がソートされたので、いくつかのデータを入力してみましょう。
惑星データへのアクセス
まず、 <body>
タグ内のコンテンツを次のように置き換えます:
<div id=Kanban></div>
Planet
レコードから HTML コンテンツを動的に生成する必要があるため、これは単にそれを配置する場所を提供するだけです。
次に、データを取得します。次のことを行う必要があります。:
Reactor で Planetレコードへのクエリを行う
Trip No
の値を持たないレコードの「未割り当て」列を作成しますこれらの各レコードの HTML 要素を [未割り当て] 列に追加します
パラメータで指定された各列について:
この列に属するレコードのクエリ応答を確認します。 (
Trip No
フィールドに基づいて、これらの各レコードの HTML 要素を列に追加します)
したがって、 <script>
タグ内のコンテンツを次のように置き換えます。:
FMLink = 'FM.link'; // This is where variables are set from the given parameters FMTableOccurrenceName = '<?Reactor bbdev_TO( $PlanetIDField ) Reactor?>'; FunctionWhereClause = '<?Reactor bbdev_Relationship( bbdev_TO( $PlanetIDField ) ) Reactor?>'; CreationValues = '<?reactor bbdev_relationshipKeyBuilder ( $FMTableOccurrenceName ) reactor?>'; PlanetIDField = '<?Reactor bbdev_Field( $PlanetIDField ) Reactor?>'; PlanetNameField = '<?Reactor bbdev_Field( $PlanetNameField ) Reactor?>'; PlanetDescriptionField = '<?Reactor bbdev_Field( $PlanetDescriptionField ) Reactor?>'; PlanetTripNoField = '<?Reactor bbdev_Field( $PlanetTripNoField ) Reactor?>'; PlanetSortNoField = '<?Reactor bbdev_Field( $PlanetSortNoField ) Reactor?>'; Columns = '<?Reactor $Columns Reactor?>'; var PlanetsArr = new Array(); // Populate columns function findPlanets() { FRTB.find( FMTableOccurrenceName + "::" + PlanetIDField, FMTableOccurrenceName + "::" + PlanetNameField, FMTableOccurrenceName + "::" + PlanetDescriptionField, FMTableOccurrenceName + "::" + PlanetTripNoField, FMTableOccurrenceName + "::" + PlanetSortNoField ).where(FunctionWhereClause).send(function(response) { for ( var i=0; i < response.data.length; i++) { ThisPlanetObj = new Object(); ThisPlanetObj.id = response.data[i][FMTableOccurrenceName + "::" + PlanetIDField]; ThisPlanetObj.name = response.data[i][FMTableOccurrenceName + "::" + PlanetNameField]; ThisPlanetObj.description = response.data[i][FMTableOccurrenceName + "::" + PlanetDescriptionField]; ThisPlanetObj.trip_no = response.data[i][FMTableOccurrenceName + "::" + PlanetTripNoField]; ThisPlanetObj.sort_no = response.data[i][FMTableOccurrenceName + "::" + PlanetSortNoField]; PlanetsArr.push(ThisPlanetObj); } buildHtml(); PlanetsArr.sort(function(a, b){ return a.sort_no > b.sort_no; }); }); } function buildHtml() { ColumnsArr = Columns.split(","); htmlString = ""; columnsIDsArray = Array(); htmlString += "<ul id='sortable_unassigned' class='connectedSortable'>"; htmlString += "<div class=Column>Unassigned</div>"; for ( var i=0; i < PlanetsArr.length; i++) { if (PlanetsArr[i].trip_no == "") { htmlString += "<li class=name id='" + PlanetsArr[i].id + "'><div>"; htmlString += PlanetsArr[i].name; htmlString += "</div><div class=desc>"; htmlString += PlanetsArr[i].description; htmlString += "</div></li>"; } } htmlString += "</ul>"; columnsIDsArray[0] = "#sortable_unassigned"; for ( var i=0; i < ColumnsArr.length; i++) { htmlString += "<ul id='sortable_" + i + "' class='connectedSortable'>"; htmlString += "<div class=Column>" + ColumnsArr[i] + "</div>"; for ( var j=0; j < PlanetsArr.length; j++) { if (PlanetsArr[j].trip_no == (i+1)) { htmlString += "<li class=name id='" + PlanetsArr[j].id + "'><div>"; htmlString += PlanetsArr[j].name; htmlString += "</div><div class=desc>"; htmlString += PlanetsArr[j].description; htmlString += "</div></li>"; } } htmlString += "</ul>"; columnsIDsArray[i+1] = "#sortable_" + i; } $('#Kanban').html(htmlString); $( columnsIDsArray.join() ).sortable({ connectWith: ".connectedSortable" }).disableSelection(); } $( function() { findPlanets(); });
かなりたくさんのコードですね。少しずつ見ていきましょう。
惑星データのクエリ Query Planet Data
function findPlanets() { FRTB.find( FMTableOccurrenceName + "::" + PlanetIDField, FMTableOccurrenceName + "::" + PlanetNameField, FMTableOccurrenceName + "::" + PlanetDescriptionField, FMTableOccurrenceName + "::" + PlanetTripNoField, FMTableOccurrenceName + "::" + PlanetSortNoField ).where(FunctionWhereClause).send(function(response) { $('#Kanban').html(""); PlanetsArr = new Array(); for ( var i=0; i < response.data.length; i++) { ThisPlanetObj = new Object(); ThisPlanetObj.id = response.data[i][FMTableOccurrenceName + "::" + PlanetIDField]; ThisPlanetObj.name = response.data[i][FMTableOccurrenceName + "::" + PlanetNameField]; ThisPlanetObj.description = response.data[i][FMTableOccurrenceName + "::" + PlanetDescriptionField]; ThisPlanetObj.trip_no = response.data[i][FMTableOccurrenceName + "::" + PlanetTripNoField]; ThisPlanetObj.sort_no = response.data[i][FMTableOccurrenceName + "::" + PlanetSortNoField]; PlanetsArr.push(ThisPlanetObj); } PlanetsArr.sort(function(a, b){ return a.sort_no > b.sort_no; }); buildHtml(); }); }
まず、次のフィールドにアクセスして検索条件を設定します。:
Planet ID (惑星ID)
Planet Name (惑星名)
Planet Description (惑星の説明)
Trip No (旅行番号)
Sort No (ソート番号)
応答が返ってきたら、応答内のさまざまなレコードをループします。レコードごとにオブジェクトを作成し、各オブジェクトに上記のプロパティを割り当て、そのオブジェクトを Planet オブジェクトの配列にプッシュします。
次に、Sort No
の値に基づいて Planet オブジェクトの配列を並べ替えます。
次に、buildHtml()
関数を実行します。
Trip Noのないレコードの列を追加し、それに惑星を割り当てる Add column for records without a Trip No and assign planets to it
ColumnsArr = Columns.split(","); htmlString = ""; columnsIDsArray = Array(); htmlString += "<ul id='sortable_unassigned' class='connectedSortable'>"; htmlString += "<div class=Column>Unassigned</div>"; for ( var i=0; i < PlanetsArr.length; i++) { if (PlanetsArr[i].trip_no == "") { htmlString += "<li class=name id='" + PlanetsArr[i].id + "'><div>"; htmlString += PlanetsArr[i].name; htmlString += "</div><div class=desc>"; htmlString += PlanetsArr[i].description; htmlString += "</div></li>"; } } htmlString += "</ul>"; columnsIDsArray[0] = "#sortable_unassigned";
次に、コンマ区切りの列のリストを取得して配列に変換します。これにより、後で簡単に反復処理を行うことができます。Unassigned
列の HTML の生成も開始します。
次に、Planet レコードの配列をループします。それぞれについて、Trip No
セットがないかどうかを確認し、そうでない場合は、これらの惑星のそれぞれに HTML を追加することによって、それを Unassigned
列に追加します。また、columnsIDArray
配列を作成し、新しい Unassigned
列を最初の値としてそこに取り込みます。後でこれを使用して、ドラッグ可能なボックスとして扱うように JavaScript ライブラリに指示します。
各列に目を通し、Planet レコードを分類します Go through each column, and categorise our Planet records
for ( var i=0; i < ColumnsArr.length; i++) { htmlString += "<ul id='sortable_" + i + "' class='connectedSortable'>"; htmlString += "<div class=Column>" + ColumnsArr[i] + "</div>"; for ( var j=0; j < PlanetsArr.length; j++) { if (PlanetsArr[j].trip_no == (i+1)) { htmlString += "<li class=name id='" + PlanetsArr[j].id + "'><div>"; htmlString += PlanetsArr[j].name; htmlString += "</div><div class=desc>"; htmlString += PlanetsArr[j].description; htmlString += "</div></li>"; } } htmlString += "</ul>"; columnsIDsArray[i+1] = "#sortable_" + i; }
最初に各Column
をループし、Unassigned
列に対して行った方法と同様に、その列の HTML の生成を開始します。
次に、Column
ごとに Planet レコードの配列をループし、Planet レコードがこの Column に属している場合は、これらの惑星ごとに HTML を追加して、その列に追加します。次の列にループする前に、columnsIDArray
に挿入します。
Then for each Column
we loop through our array of Planet records, and if a Planet record belongs in this Column
we add it to that column by appending the HTML for each of these planets. Before we loop to the next Column
we pop it onto our columnsIDArray
.
ドラッグ可能なリストをインスタンス化する Instantiate our draggable list
$('#Kanban').html(htmlString); $( columnsIDsArray.join() ).sortable({ connectWith: ".connectedSortable" }).disableSelection();
次に、生成された HTML を含む変数を <div id="Kanban">
要素に配置するよう JavaScript に指示します。
(HTML に入力するために使用している手法について簡単に説明します。これは非常に原始的です。JQuery ライブラリを使用して、HTML 要素を変更または追加するためのはるかに洗練された方法がありますが、この手法を採用しています。単一の文字列ビットごと - 実際に何をしているかを明確にします)。
次に、列 ID の配列 (これは HTML 要素の ID) で .sortable()
メソッドをインスタンス化するように JQuery ライブラリに指示しますが、最初に join()
メソッドを使用して列 ID の配列を列 ID のコンマ区切りリスト - これはメソッドが期待する形式です。これがプラネット ボックスを「アクティブ化」し、ドラッグ可能にします。
ここで、JavaScript が上記の JavaScript と一致することを確認し、BlackBox を再コンパイルして、[プレビュー] タブで次のパラメーターを設定します。:
素晴らしい!
惑星は、Trip No
値が設定されているかどうかに応じて、異なる列の下に表示される場合があります。何も設定されていない場合は、未割り当ての下にすべて表示されます。
まったく異なるもの、またはまったく何も表示されない場合は、上記のように JavaScript を正確に入力したことを確認してください。必要なすべての JavaScript ライブラリをインポートしたことを確認してください。順序も重要です。 JQuery-UI ライブラリをインポートする前に、コア JQuery ライブラリをインポートする必要があります。
それができない場合は、FM データビューアに次のコードを入力してデバッグ モードに入ります。:
Web ビューアの右下にある Reactor アイコンをクリックし、Browser
をクリックします。
これにより、デフォルトの Web ブラウザで BlackBox が開き、開発者ツールを使用してデバッグできます。
ソート順の変更 Changing sort order
これで、かんばんボードに Planet レコードを取り込み、正しい列に並べ替えることができました。これらの惑星を移動することはできますが、これらの変更は記録されません。次に Webビューアを読み込むと、変更は失われます (FileMaker Pro でデータを元に戻さない限り)
最初に必要なのは、Planet が移動したときにインターセプトする方法です。これは、イベントハンドラーを使用して実行できます。
イベントとは、ユーザまたはブラウザが行うことです。ハンドラーを特定のイベントにアタッチして、イベントがいつ発生したか、および何が起こったかの詳細をキャプチャできます。
続行するには、ドキュメントを参照する必要があります。
https://api.jqueryui.com/sortable/#event-receive
https://api.jqueryui.com/sortable/#event-stop
この 2 つのイベントは、私たちにとって有益です。惑星が別のリストに移動すると、recieve
受信イベントが発生し、Trip No
とSort No
を更新する必要があります。惑星が独自のリスト内で移動すると、stop
停止イベントが発生し、Sort No
を更新する必要があります。
イベントをキャプチャするには、次のように変更します。:
$( columnsIDsArray.join() ).sortable({ connectWith: ".connectedSortable" }).disableSelection();
これに:
$( columnsIDsArray.join() ).sortable({ connectWith: ".connectedSortable", receive: function (event,ui) { // Do something } }).disableSelection();
それでは、Planet を別の場所にドラッグするときに FileMaker に伝える必要があることについて考えてみましょう。まず、割り当てられている列を変更しているので、それを書き戻す必要があります。しかし、同様に重要なことは、ターゲット列のソート順を効果的に変更したことです。これは、ドラッグ先の下にある惑星がソート順で押し下げられるようになったためです。
そのため、移動したばかりのレコードを含む、ターゲット列のすべてのレコードの並べ替え順序を書き込む必要があります。
これを行うには、ターゲット列の内容を知る必要があります。 column 要素に属する子要素を調べることで、これを行うことができます。:
$("#" + event.target.id)[0].childNodes[3].id;
これは、ターゲットの下にある 3 番目の惑星のレコード ID を返します。これがどのように機能するかわかりませんか?
Planet レコードをクエリした後に HTML を生成したときに、Planet の要素に対して ID を保存しました。:
htmlString += "<li class=name id='" + PlanetsArr[j].id + "'><div>";
そこで、列の HTML 要素を見て、子要素を見て、それぞれの PlanetID を調べます。
したがって、これらの ID をそれぞれループして、並べ替え順序を更新する必要があります。 1 から始めて、毎回 1 ずつ増やします。
したがって、.sortable()
メソッド呼び出しを次のように置き換えます。:
$( columnsIDsArray.join() ).sortable({ connectWith: ".connectedSortable", stop: saveLocation, receive: saveLocation, }).disableSelection();
これにより、stop
停止イベントと receive
受信イベントの両方に対して同じ関数が呼び出されます。
これより前に、JavaScript のどこかに関数を記述します。最適な場所は、findPlanets()
関数の前です。
function saveLocation(event,ui) { NewTripNo = PlanetID = $("#" + event.target.id)[0].childNodes[0].innerText.replace("Trip ",""); for (var i=1; i <= $("#" + event.target.id)[0].childNodes.length; i++) { PlanetID = $("#" + event.target.id)[0].childNodes[i].id; FRTB.update( [FMTableOccurrenceName + "::" + PlanetSortNoField, i], [FMTableOccurrenceName + "::" + PlanetTripNoField, NewTripNo] ).filter( FMTableOccurrenceName + "::" + PlanetIDField + "=" + PlanetID ).send(); } }
次に、それが何をするかについての義務的なステップについて説明します。まず、Planet を移動した番号の旅行番号を抽出します。これを行うには、Column HTML 要素 (実際に表示される列テキスト) のテキスト名を取り出し、実際の旅行番号を解析します。 Unassigned
列の場合は、空白の値が設定されるようにします。
次に、この列内のすべての Planet をループしてその ID を取得し、各 ID に基づいて各レコードのソート番号とトリップ番号を更新します。
並べ替え番号については、現在の「for ループ」値を送信するだけです。これにより、列の下に表示される順序が各レコードの相対的な順序番号になります。
したがって、再コンパイルする場合は、プレビュー タブに切り替えてください。行った変更はすべて永続化されます。惑星をリスト内の別のスペースに移動すると、新しい順序が記憶されます。惑星を別の列に移動すると、現在はその列の下にあり、その列内での順序が記憶されているはずです。別のタブに切り替えてから元に戻しても、同じままです。
BlackBox がこれをまったく覚えていない場合は、関数が上記のとおりであることを確認し、上記のスニペットのようにイベントに対して設定します。
ここで停止できます。これにより、惑星が FileMaker から BlackBox に取り込まれ、それらを移動して、変更を FileMaker に書き戻すことができます。そのため、次にロードするときに、すべてをどこに置いたかが記憶されます。
ただし、少し考えてみましょう。FileMaker Server でホストされているデータベース用にこれを構築しているとしましょう。かんばんボードを使用してさまざまな展示会を計画しており、もう 1 人の Space Trip Planner が Venus を Trip 2 に移動します。かんばんボードをリアルタイムで更新するには、ポーリングを使用する必要があります。
変更をポーリング Polling for changes
これで、BlackBox とのやり取りが永続的であることを確認できます。これは単なる見せかけではなく、実際に何かを行っているためです。
ある日、新しい惑星が発見されたらどうしますか?何か恐ろしいことが起こり、私たちの惑星の 1 つが消えたらどうしますか?惑星の名前/説明が変わるとどうなりますか?その場合、BlackBox には古い情報が表示されます。
レコードの変更を検出するには、Planet データをすべて取り込む元のクエリに .poll()
メソッドを適用し、次の行を置き換えます。:
).where(FunctionWhereClause).send(function(response) {
これに:
).where(FunctionWhereClause).poll(pollChanges).send(function(response) {
これは、クエリが完了すると、変更をポーリングし続けるように Reactor に指示します (デフォルトは 5 秒ごと)。
新しい惑星が発見されたら、関連する列に入力する必要があります。しかし、それを列のどこに置くのでしょうか?Sort No
は別のユーザによって設定された可能性があり、その列の他のすべての惑星の位置に影響を与える可能性があります。したがって、安全にプレイするには、その列に属するすべての惑星を再クエリし、それらの順序に基づいて再リストする必要があります。
既存の惑星に新しい名前/説明が付けられた場合、それらの新しい詳細を表示する必要があります。しかし、Trip No
が変更された場合はどうなるでしょうか?次に、元の列を更新して削除し、ターゲット列を更新して追加する必要があります。 Sort No
が変更された場合、新しい惑星と同様に、両方の列を再リストする必要があります。
既存の惑星が消えて削除されたらどうなりますか?列を削除するには、列を更新する必要があります。
これらのポーリング シナリオを実際に調整して、関連する UI 要素を更新できます。例えば:
新しい惑星
該当する列に追加します。
Sort No
が設定されていない場合は、それを列の一番下に置き、その列のすべての惑星のSort No
の値を書き戻します。Sort No
が設定されている場合は、列の適切な場所に配置する必要があります。
更新された惑星
既存の惑星の詳細が変更された場合、
Sort No
が変更された可能性があります。その列の惑星の順序が正しいことを確認するには、Sort No
と上/下の惑星のSort No
を確認する必要があります。隣接する惑星の上/下にある必要があり、そうでない場合は、それらを切り替えてから、その上/下の惑星を再度確認し、正しい位置に到達するまでこの比較を続けます。
削除された惑星
これははるかに簡単です。惑星が消えた場合は、関連する列から削除するだけです。
私たちはどこであなたを失いましたか?その混乱のどこかであなたを失ったと思います。
ポーリングでこれらすべてのやり取りを強制すると、このガイドは耐えられないほど長くなります。率直に言って、それは必要ありません。代わりに、元の対象レコードから新しいレコード、更新されたレコード、または削除されたレコードがあることを Reactor が報告するたびに、かんばんボードを再描画します。
したがって、ポーリング関数を次のように記述します。:
function redrawBoard(results) { if (OverridePoll) return; if (results.remove.length > 0 || results.create.length > 0 || results.update.length > 0) { findPlanets(); } }
そして、ポーリングが新しい/変更された/削除されたレコードを検出したときに、この関数を実行するように最初のクエリに指示します。:
).where(FunctionWhereClause).poll(redrawBoard).send(function(response) {
では、私たちの関数は何をするのでしょうか? とても簡単です。ポーリングの結果を自動的に通過します。これは、行 ID の 3 つの配列を含むオブジェクトです。新しいレコード用に 1 つの配列、変更されたレコード用に 1 つの配列、および削除されたレコード用に 1 つの配列。行 ID とはこれは、FileMaker がレコードを識別するために内部で使用する番号です。Reactor クエリは、ユーザ が要求したかどうかに関係なく、その応答に行 ID を自動的に含めます。
どのレコードが変更/追加/削除されたかを知る必要はありません。知る必要があるのは、変更/追加/削除されたレコードがあることだけです。したがって、3 つの配列の長さをチェックして、値を持つものがあるかどうかを確認するだけで十分です。
変更/追加/削除されたレコードがある場合、findPlanets()
関数を実行するだけで、BlackBox が最初に読み込まれたときと同じように、データが再クエリされて表示されます。
ただし、まだ準備ができていません。データを書き戻す場合は、ポーリングの変更を無視する必要があります。なんで?この一連のイベントについて考えてみてください。:
惑星をかんばんボードのどこか別の場所に動かす
変更を FileMaker に書き戻す
これによりポーリングがトリガーされ、コンテンツの再読み込みがトリガーされます
「ドラッグ」アクションの途中でコンテンツが再読み込みされ、アクションが終了する
これが、関数の開始時に OverridePoll
変数をチェックする理由です。
saveLocation()
関数では、OverridePoll
変数を true
に設定する必要があります。:
function saveLocation(event,ui) { var OverridePoll = true; ...}
And set it to false when the FRTB.update()
function is completed:
.send(function(response) { var OverridePoll = false; });
And since the variable is used in multiple functions we need to define it globally. So if you add it to our global variables before our functions, any function will have access to it:
... Columns = '<?Reactor $Columns Reactor?>'; var PlanetsArr = new Array(); var OverridePoll = false;
There, now we have an option to Override polling. This means while this Override is activated, any polling will be ignored, so we activate it at the start of our action that writes to FileMaker, and we deactivate it when it has been written.
So now if you pop into the data layout for Planets, you’ll be able to edit the raw data and should see the Kanban board changing before your eyes.
You’ve successfully taken a dynamic JavaScript library, and implemented it using Reactor to communicate with FileMaker. Any visual interface you wish to create in Reactor involves these core principles:
Read data into your BlackBox
Intercept events that should change data, and write changes back to FileMaker (adding a record, updating a record, editing record fields, or even running a script - though that isn’t part of this guide)
Show data changes in real time
Any BlackBox comes down to these things. Some only read data, same also write data back, others also use polling.
Were we being a bit presumptuous in arrogantly declaring your BlackBox is working? Luckily we prepared one earlier. Check out our Kanban BlackBox that comes included in our Reactor Core, or just download our completed BlackBox file and see where you went wrong:
https://www.dropbox.com/s/lz3p751qezikjsu/PlanetKanban.rbb?dl=0
Import it into your Reactor, by clicking ‘Import a new BlackBox’ when creating a new BlackBox. Just be sure to rename the BlackBox and function if they’re the same name as yours - otherwise Moogie will get confused.