...
一般的な Reactor ガイドと FRToolbox リファレンス ドキュメントでは、Reactor が提供できる一連の機能について概説しています。ただし、FileMaker が提供できる一連の機能について解説しています。ただし、FileMaker で実現できないことは実際には実現していません。
Reactor の本来の力は、ダイナミックライブラリを採用した完全にインタラクティブな JavaScript ユーザーインターフェイスで Reactor の機能を使用するときに発揮されます。
...
Code Block |
---|
$( columnsIDsArray.join() ).sortable({ connectWith: ".connectedSortable", receive: function (event,ui) { // Do something } }).disableSelection(); |
So let’s have a think about what we need to tell FileMaker when dragging a Planet to a different location. First off, we’re changing what column it’s assigned to, so we need to write that back. But just as importantly, we have effectively changed the sort order in the target column, as the Planets that sit below where we’re dragging to are now pushed down the sort order.
So we need to write the sort order of all records in the target column, including the record we’ve just moved.
To do this, we need to know the contents of our target column. We can do this by looking at the child elements that belong to the column element:
$("#" + event.target.id)[0].それでは、Planet を別の場所にドラッグするときに FileMaker に伝える必要があることについて考えてみましょう。まず、割り当てられている列を変更しているので、それを書き戻す必要があります。しかし、同様に重要なことは、ターゲット列のソート順を効果的に変更したことです。これは、ドラッグ先の下にある惑星がソート順で押し下げられるようになったためです。
そのため、移動したばかりのレコードを含む、ターゲット列のすべてのレコードの並べ替え順序を書き込む必要があります。
これを行うには、ターゲット列の内容を知る必要があります。 column 要素に属する子要素を調べることで、これを行うことができます。:
Code Block |
---|
$("#" + event.target.id)[0].childNodes[3].id; |
...
This would return the record ID of the third planet below the target. Not sure how this works?
When we generated our HTML after querying our Planet records, we stored the ID against the element for the Planet:
...
これは、ターゲットの下にある 3 番目の惑星のレコード ID を返します。これがどのように機能するかわかりませんか?
Planet レコードをクエリした後に HTML を生成したときに、Planet の要素に対して ID を保存しました。:
Code Block |
---|
htmlString += "<li class=name id='" + PlanetsArr[j].id + "'><div>"; |
So we look at the HTML element of our column, look at the child elements, and inspect each for our PlanetID.
So now we just need to loop through each of these IDs, and update the sort order. We can start at 1, and increase it by 1 every time.
So replace our .sortable()
method call with this:
$( そこで、列の HTML 要素を見て、子要素を見て、それぞれの PlanetID を調べます。
したがって、これらの ID をそれぞれループして、並べ替え順序を更新する必要があります。 1 から始めて、毎回 1 ずつ増やします。
したがって、.sortable()
メソッド呼び出しを次のように置き換えます。:
Code Block |
---|
$( columnsIDsArray.join() ).sortable({ |
...
connectWith: ".connectedSortable", |
...
stop: saveLocation, |
...
receive: saveLocation, |
...
}).disableSelection(); |
This will call the same function for both stop
and receive
events.
Write the function somewhere in our JavaScript prior to this - the best place would be before the findPlanets()
function.
function これにより、stop
停止イベントと receive
受信イベントの両方に対して同じ関数が呼び出されます。
これより前に、JavaScript のどこかに関数を記述します。最適な場所は、findPlanets()
関数の前です。
Code Block |
---|
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(); |
...
}
}
Now for our obligatory step through what it does. First we the extract trip number of the number we’ve moved our Planet to - to do this we pull out the text name of the Column HTML element (the column text that actually displays), and parse out the actual trip number. If it’s our Unassigned
column we ensure that a blank value will be set.
Then we loop through every Planet within this column, grab its ID, and update the sort number, and trip number, for each record based on each ID.
For the sort number, we just send through the current “for loop” value, this will ensure the order they appear under the column will be the relative order numbers for each record.
So if you recompile, switch to the preview tab, any changes you make should be persistent. Move a planet to a different space in the list, and it will remember the new order. Move a planet to a different column, it should remember that it’s now under the column, and its order within that column - you should be able to switch to a different tab, then switch back, and it will remain the same.
If your BlackBox doesn’t remember any of this, ensure your function is exactly as written above, and you set it against the events as in the snippet above that.
We could stop right here. It brings our Planets from FileMaker into our BlackBox, and allows us to move them around and have the changes write back to FileMaker. So next time it loads it will remember where you left everything.
However, let’s ponder for a moment - let’s say we’re building this for a database hosted on FileMaker Server. We’re using the Kanban board to plan our various exhibitions, and another one of our Space Trip Planners moves Venus into Trip 2. If we want your Kanban board to update in real-time we’ll need to employ polling.
Polling for changes
Now we’re ensuring that our interactions with our BlackBox are persistent, it’s not just for show - it’s actually doing something.
What if one day we discover a new planet? What if something terrible happens and one of our planets disappears? What if the name/description of a planet changes? Then our BlackBox would be showing out-of-date information.
To pick up on record changes, apply the .poll()
method to our original query that brings in all the Planet data, and replace this line:
).where(FunctionWhereClause).send(function(response) {
With this:
).where(FunctionWhereClause).poll(pollChanges).send(function(response) {
This instructs Reactor so that once the query is complete, it will continue to poll for changes (default is every 5 seconds).
When a new planet is discovered we need to populate it into the relevant column. But where in the column do we put it? The Sort No
could have been set by another user, which could affect the position of all other Planets in that column. So to play it safe, we need to re-query all Planets that belong to that column, and re-list them based on their order.
When an existing planet gets a new name/description, we we need to show those new details. But what if the Trip No
changes? Then we need to update the original column to remove it, and update the target column to add it. What if the Sort No
changes, then similar to new planets, we need to re-list both columns.
What if an existing planet disappears, and is deleted? We’d need to update the column to remove it.
We could really tailor these polling scenarios to update the relevant UI elements. For example:
New Planet
Add it to the relevant column. If it has no
Sort No
set, put it at the bottom of the column, and write back theSort No
values of all Planets in that column. If it does have aSort No
set, we’d need to place it in the appropriate place in the column.
Updated Planet
When an existing planet has details changed, it’s possible that the
Sort No
could have changed, so to ensure the order of the Planets in that column are correct we’d need to check itsSort No
verses theSort No
of the planets above/below it. If it should be above/below an adjacent Planet and it’s not, then they should be switched, and then check the Planet above/below it again - and keep doing this comparison until its reached its correct location.
Deleted Planet
This one’s much simpler. If a planet disappears, we simply need to remove it from its relevant column
Where did we lose you? We assume we lost you somewhere in that confusion.
If we were to enforce all these interactions in our polling, this guide would get insufferably long - and frankly it’s not necessary. Instead, we’ll simply redraw the Kanban board any time Reactor reports that there are new records, updated records, or deleted records, from our original found set.
So write a polling function as so:
function redrawBoard(results) {
if (OverridePoll) return;
if (results.remove.length > 0 || results.create.length > 0 || results.update.length > 0) {
findPlanets();
}
}
And tell our initial query to run this function when polling detects a new/changed/removed record:
).where(FunctionWhereClause).poll(redrawBoard).send(function(response) {
So what does our function do? It’s quite simple. It automatically gets passed through the results of the polling. This is an object containing three arrays of row ID’s. One array for new records, one array for changed records, and one array for deleted records. What is a row ID? It’s a number that FileMaker uses internally to identify records, any Reactor query automatically includes the row ID in its response, regardless of whether or not you asked for it.
However in this example we don’t need to know what records have been changed/added/deleted, all we need to know is that there are records that have been changed/added/deleted. So simply checking the lengths of the three arrays to see if any have values will do, for our purposes.
If there are records changed/added/deleted we simply run our findPlanets()
function, this re-queries the data and displays it - the same as when the BlackBox first loads.
However it’s not ready yet - we need to ignore any polling changes when we’re the ones writing the data back. Why? Think about this sequence of events:
We move a planet to somewhere else in the Kanban board
It writes the change back to FileMaker
This triggers polling, triggering the reloading of the content
The content reloads in the middle of the ‘dragging’ action, halting the action
This is why we check the OverridePoll
variable at the start of the function.
In our saveLocation()
function, we need to set the OverridePoll
variable to 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.
...
}
} |
次に、それが何をするかについての義務的なステップについて説明します。まず、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()
メソッドを適用し、次の行を置き換えます。:
Code Block |
---|
).where(FunctionWhereClause).send(function(response) { |
これに:
Code Block |
---|
).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 が報告するたびに、かんばんボードを再描画します。
したがって、ポーリング関数を次のように記述します。:
Code Block |
---|
function redrawBoard(results) {
if (OverridePoll) return;
if (results.remove.length > 0 || results.create.length > 0 || results.update.length > 0) {
findPlanets();
}
} |
そして、ポーリングが新しい/変更された/削除されたレコードを検出したときに、この関数を実行するように最初のクエリに指示します。:
Code Block |
---|
).where(FunctionWhereClause).poll(redrawBoard).send(function(response) { |
では、私たちの関数は何をするのでしょうか? とても簡単です。ポーリングの結果を自動的に通過します。これは、行 ID の 3 つの配列を含むオブジェクトです。新しいレコード用に 1 つの配列、変更されたレコード用に 1 つの配列、および削除されたレコード用に 1 つの配列。行 ID とはこれは、FileMaker がレコードを識別するために内部で使用する番号です。Reactor クエリは、ユーザ が要求したかどうかに関係なく、その応答に行 ID を自動的に含めます。
どのレコードが変更/追加/削除されたかを知る必要はありません。知る必要があるのは、変更/追加/削除されたレコードがあることだけです。したがって、3 つの配列の長さをチェックして、値を持つものがあるかどうかを確認するだけで十分です。
変更/追加/削除されたレコードがある場合、findPlanets()
関数を実行するだけで、BlackBox が最初に読み込まれたときと同じように、データが再クエリされて表示されます。
ただし、まだ準備ができていません。データを書き戻す場合は、ポーリングの変更を無視する必要があります。なんで?この一連のイベントについて考えてみてください。:
惑星をかんばんボードのどこか別の場所に動かす
変更を FileMaker に書き戻す
これによりポーリングがトリガーされ、コンテンツの再読み込みがトリガーされます
「ドラッグ」アクションの途中でコンテンツが再読み込みされ、アクションが終了する
これが、関数の開始時に OverridePoll
変数をチェックする理由です。
saveLocation()
関数では、OverridePoll
変数を true
に設定する必要があります。:
Code Block |
---|
function saveLocation(event,ui) {
var OverridePoll = true;
...} |
FRTB.update()
関数が完了したら、false に設定します。:
Code Block |
---|
.send(function(response) {
var OverridePoll = false;
}); |
また、変数は複数の関数で使用されるため、グローバルに定義する必要があります。したがって、関数の前にグローバル変数に追加すると、どの関数もそれにアクセスできます。:
Code Block |
---|
...
Columns = '<?Reactor $Columns Reactor?>';
var PlanetsArr = new Array();
var OverridePoll = false; |
そこに、ポーリングをオーバーライドするオプションがあります。つまり、この Override が有効化されている間はポーリングが無視されるため、FileMaker への書き込みアクションの開始時に有効化され、書き込まれたときに無効化されます。
これで、Planets のデータ レイアウトに飛び込むと、生データを編集できるようになり、かんばんボードが目の前で変化するのを確認できるはずです。
動的な JavaScript ライブラリを取得し、Reactor を使用して実装して FileMaker と通信することに成功しました。 Reactor で作成するビジュアル インターフェイスには、次の基本原則が含まれます。:
BlackBox にデータを読み込む
データを変更する必要があるイベントをインターセプトし、変更を FileMaker に書き戻します (レコードの追加、レコードの更新、レコードのフィールドの編集、またはスクリプトの実行 - それはこのガイドの一部ではありませんが)。
データの変更をリアルタイムで表示
すべての BlackBox は、これらのことに帰着します。データを読み取るだけのものもあれば、データを書き戻すものもあれば、ポーリングを使用するものもあります。
BlackBox が機能していると傲慢に宣言することは、少し傲慢でしたか? 幸いなことに、以前に準備しました。 Reactor Core に含まれている Kanban BlackBox を確認するか、完成した BlackBox ファイルをダウンロードして、どこが間違っていたかを確認してください。:
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.新しい BlackBox を作成するときに [新しい BlackBox をインポート] をクリックして、Reactor にインポートします。 BlackBox と関数が同じ名前である場合は、必ず名前を変更してください。そうしないと、Moogie が混乱します。