[macOS] スタンドアロンの FileMaker Pro と localhost との連携 vol.3

## 使用環境
- Claris FileMaker Pro 21.1.41 (macOS)
- macOS Sequoia 15.3.1

## Web ビューアと JavaScript の通信

 前回、Mac で、スタンドアロンの FileMaker Pro との連携だけを目的にした localhost が立ち上げるということをやりました。
 これによって、FileMaker レイアウトは、HTML/CSS で作れば良いということになったわけです。
 
 そして、現時点で積み残しているのが以下の点です。
 - FileMaker Pro と localhost 上のコンテンツ間のデータのやり取り
 - localhost 上のコンテンツでのページ遷移の FileMaker Pro への反映

 今回は、まず、**FileMaker Pro と localhost 上のコンテンツ間のデータのやり取り**について取り組みたいと思います。

  サンプルとして、まず、「localhost 上のコンテンツ」が必要なので、簡単なフォームを作ってみます。
 localhost 上、前回の設定だとしたら、`~/Sites/WebServer/Documents/` または、シンボリックリンクを作っていましたから、`~/htdocs/` に新しいディレクトリを作ります。

 ここでは、`fmsite1.com/` とします。ここには、`http://localhost/fmsite1.com/` または、設定したローカルホスト名で、`http://（ローカルホスト名）/fmsite1.com/` で接続できるようになります。
 蛇足ですが、ローカルホスト名を設定しておけば、LAN 内の別のデバイスからも接続できますので、意味があるかは別として、スタンドアロンの FileMaker Pro 運用マシンと localhost 運用マシンを分けることもできます。

 ここに、`simple-form.html` を作成します。
 現状では、`http://localhost/fmsite1.com/` にアクセスした場合、`autoindex` が効いているので、`Index of fmsite1.com` と表示されて、`simple-form.html` が見えてると思います。
 ![スクリーンショット 2025-03-06 8.38.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/bbfdd113-b967-4ae4-a4d0-19efe3d8deae.png)

 このようにドキュメントルートの直下から作り始めるのではなく、もう１階層下から始める運用をしておくと、あたかもマルチドメインを扱ってるが如く、目的ごとにレイアウト用のページを作れるので、localhost のコンテンツの複雑さの軽減につながります。
 この記事では、このスタイルでやっていきます。

 `simple-form.html` の内容は、Bootstrap 5.3.3 を CDN を使って簡単に作ってしまいます。
```.html:simple-form.html
<!DOCTYPE html>
  <html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="{$description}">
    <meta name="author" content="{$author}">
    <title>FileMaker Pro 用フォーム</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-QWTKZyjpPEjISv5WaRU9OFeRpok6YctnYmDr5pNlyT2bRjXh0JMhjY6hW+ALEwIH" crossorigin="anonymous">
</head>

<div class="container-fluid d-flex justify-content-center mt-5">
    <div class="row">
        <div class="card" style="width: 22rem;">
            <div class="card-body">
              <h5 class="card-title">Simple Form</h5>
              <p class="card-text">FileMaker Pro 用フォーム</p>
            </div>
            <div class="card-body">
                <div class="form-floating mb-3">
                    <input type="text" class="form-control" id="itemName" placeholder="ぐんぐんグルトα">
                    <label for="itemName">名称</label>
                </div>

                <div class="form-floating">
                    <input type="text" class="form-control" id="productCd" placeholder="806">
                    <label for="productCd">製品コード</label>
                </div>

                <div class="form-floating">
                    <input type="number" class="form-control" id="quantity" placeholder="1">
                    <label for="quantity">数量</label>
                </div>

                <div class="form-floating">
                    <input type="text" class="form-control" id="unitPrice" placeholder="180">
                    <label for="unitPrice">単価</label>
                </div>

                <div class="form-floating">
                    <input type="text" class="form-control" id="amount" placeholder="180">
                    <label for="amount">金額</label>
                </div>

                <button type="submit" class="btn btn-primary mt-3" onclick="submitWebForm()">FileMaker データベースを更新</button>
            </div>
          </div>
    </div>
</div>

<body>

    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-YvpcrYf0tY3lHB60NNkmXc5s9fDVZLESaAA55NDzOxhy9GkcIdslK1eN7N6jIeHz" crossorigin="anonymous"></script>
</body>
</html>
```
![スクリーンショット 2025-03-06 19.33.51.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/92f4867e-d7f9-476e-8e28-c9ee051f117e.png)


 これは、Bootstrap の Floating label を使っています。
 Floating label を使う場合、表示はされませんが、`placeholder` 属性が必須です。
 また、`id` 属性も必要になりますが、これは、FileMaker Pro 側でも必要です。

## 対応する FileMaker テーブルの作成

 管理のし易さから考えたら、原則、`id` 属性をテーブル名にするのが良いでしょう。他の言語との連携や SQL の利用を考えたときは、日本語フィールド名は煩わしいので避けた方が良いと思います。FileMaker のフィールドにはコメントを書けるので、コメントを活用しましょう。

 FileMaker テーブルは、上記のコンテンツに合わせて作ります。（本来は、FileMaker テーブルに合わせて、HTML コンテンツを作るというのが、この記事の趣旨からは正解です）
 ここでは簡単に以下のように作ってみました。
 ![スクリーンショット 2025-03-06 9.29.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/49fcd136-deea-4026-abd7-714989bfb0d1.png)

 これで土台は出来ました。

 ## FileMaker Pro から localhost への通信

 ### FileMaker Pro レイアウトの準備

  どのように、FileMaker Pro と localhost との間で通信を行うかですが、
  まず、FileMaker Pro → localhost の通信は、FileMaker 側では、
1. `Web ビューア`のあるレイアウトに localhost 側へ送りたいフィールドを配置する
1. `Web ビューアで JavaScript を実行` スクリプトステップをスクリプトトリガ `OnRecordLoad` および `OnRecordCommit` で起動させる

 簡単にいうと、**レコードのロードまたはコミットで、`Web ビューアで JavaScript を実行` スクリプトステップを起動する。起動するからには、そのレイアウトに対象フィールドが存在しなければならない**ということです。

 この動作に対応する `Web ビューア`は、以下のように設定する必要があります。
- オブジェクト名を設定する
- ✅ Web ビューア内容とのインタラクションを許可
- ✅ JavaScript による FileMaker スクリプトの実行を許可

また、直接関係ありませんが、「✅ URL を自動でエンコード」としておいた方がいいでしょう。
![スクリーンショット 2025-03-06 9.48.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/145e83e0-4912-4b31-a0f2-72461dc0b28d.png)

![スクリーンショット 2025-03-06 9.49.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/6c3d85ed-6303-46f0-869e-1db378f7b29b.png)

 上記を踏まえて、以下のようにレイアウトを作ります。

- レイアウト名: localhost のコンテンツと同一の名称。この場合、`simple-form.html`
- Web ビューアのオブジェクト名: simpleForm
```.calc:Web ビューアのカスタム Web アドレス
"http://localhost/fmsite1.com/" &
Get ( レイアウト名 )
```
![スクリーンショット 2025-03-06 10.01.05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/b9a21a76-1908-4cce-a0ca-bdf1336f31b9.png)

こうしておくことで、Web ビューア自体の設定は、別のレイアウトを作る時も最小限に抑えられます。
レイアウト名 = localhost 側のファイル名またはディレクトリ名としておくことで、一々、カスタム Web アドレスの項目を設定しなくて済みます。レイアウト名 = ディレクトリ名 とするときは、対象は、そのディレクトリのインデックスファイルということになります。

　また、FirstWindowOpen のトリガで、デバイスとプラットフォームを取得することで、カスタム Web アドレスにハードコードしなくても、起動スクリプトで設定しておくことも可能です。

 現在、フィールドを配置している領域は、完成後は不可視にします。開発中は、見えてた方が作りやすいということです。

![スクリーンショット 2025-03-06 10.02.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/6d09caf7-c8d3-4429-ae1b-dc9ddade7c58.png)

 ブラウズモードでは、次のようになります。
![スクリーンショット 2025-03-06 19.44.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/de013175-2260-40d5-bb2a-e3063339076e.png)

### FileMaker フィールドからの送信

 次に、このレイアウトのレコードがロード、または、コミットされたときに起動するスクリプトを作成します。

```.vb:sendData
ユーザによる強制終了を許可 [ オフ ]
# 
変数を設定 [ $json ; 値: JSONSetElement ( 	"{}" ; 	[ "itemName"		; SimpleForm::itemName	; JSONString ] ; 	[ "productCd"	; SimpleForm::productCd	; JSONString ] ; 	[ "quantity"		; SimpleForm::quantity	; JSONNumber ] ; 	[ "unitPrice"	; SimpleForm::unitPrice	; JSONNumber ] ; 	[ "amo… ]
# 
スクリプト一時停止/続行 [ 間隔（秒）: .002 ]
Web ビューアで JavaScript を実行 [ オブジェクト名: "simpleForm" ; 関数名: "setFmData" ; 引数: $json ]
```
 `スクリプト一時停止/続行` スクリプトステップの間隔は、コンテンツ内容や環境に左右されるので、適宜、調整してください。

 `変数を設定` スクリプトステップの値は以下の計算式です。

```.calc:$json の計算式
JSONSetElement ( 
	"{}" ;
	[ "itemName"	; SimpleForm::itemName	; JSONString ] ;
	[ "productCd"	; SimpleForm::productCd	; JSONString ] ;
	[ "quantity"	; SimpleForm::quantity	; JSONNumber ] ;
	[ "unitPrice"	; SimpleForm::unitPrice	; JSONNumber ] ;
	[ "amount"		; SimpleForm::amount	; JSONNumber ]
)
```

　各フィールドのデータをフィールド名と同名のキーにして JSON オブジェクトにしています。
 これを localhost 側に渡すということです。

 上記を、レイアウトに戻って、レイアウト設定で、スクリプトトリガ `OnRecordLoad` および `OnRecordCommit` に、作成したスクリプト `sendData` を設定します。
 ![スクリーンショット 2025-03-06 19.54.33.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/24dc6d91-4867-478e-8afc-f8c730ea34c4.png)

 これで、FileMaker Pro から、localhost 側のコンテンツへのデータ送信はレコードのロード、または、コミットごとに行われています。

## localhost 側での FileMaker Pro からのデータの受信

 FileMaker Pro からのデータの受信は、JavaScript を使用します。`simple-form.html` の最後で、このコンテンツ用の JavaScript ファイルを読み込むようにしましょう。ついでに、JavaScript 用のディレクトリも作っておきましょう。
```.html:simple-form.html の最下行
    <script src="./js/simple-form.js"></script>
</body>
</html>
```

 js ディレクトリ内に、このコンテンツ用の JavaScript ファイルを作ります。
```.js:~/Sites/WebServer/Documents/fmsite1.com/js/simple-form.js
'use strict';

const setFmData = (parameterJson) => {
    const parameters = JSON.parse(parameterJson);

    document.getElementById("itemName").value   = parameters['itemName']; 
    document.getElementById("productCd").value  = parameters['productCd']; 
    document.getElementById("quantity").value   = parameters['quantity'];
    document.getElementById("unitPrice").value  = parameters['unitPrice'];
    document.getElementById("amount").value     = parameters['amount'];
}

```

 この関数を作った段階で、FileMaker Pro からの送信と localhost 側の受信はできています。
  FileMaker フィールドにデータを入れて確定（この場合、フィールド外をクリック）すると、Web ビューアで表示されている localhost のコンテンツも更新されます。
![スクリーンショット 2025-03-06 19.52.47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/ba58faf1-3376-4b73-bb68-2eca78c8e8a5.png)

## localhost 側から、FileMaker Pro への送信

 目的は、localhost で FileMaker レイアウトを作ることですから、今までの作業は、完成版では不可視となります。localhost で作った HTML/CSS で、データ入力し、それが FileMaker データベースに反映されなければ意味がありません。

### localhost から FileMaker への送信

 HTML/CSS の Web フォームにデータを入力し、サブミットボタンをクリックすることで、FileMaker への送信が行えるようにします。`simple-form.js` に以下の関数を追加します。

```.js:~/Sites/WebServer/Documents/fmsite1.com/js/simple-form.js
function submitWebForm ()
{
    const itemName  = document.getElementById("itemName").value;
    const productCd = document.getElementById("productCd").value;
    const quantity  = document.getElementById("quantity").value;
    const unitPrice = document.getElementById("unitPrice").value;
    const amount    = document.getElementById("amount").value;

    const parameters    = {
        itemName,
        productCd,
        quantity,
        unitPrice,
        amount
    };

    const parameterJSON = JSON.stringify(parameters);
    FileMaker.PerformScriptWithOption("StoreWebData", parameterJSON, "0");
}

```

 この関数は、id を使って、Web フォームの各フィールドを読み取り、JSON のキーと同名の変数にセット、それを使って、JSON を作り、FileMaker 側のスクリプトを起動するという動作をします。
 `FileMaker.PerformScriptWithOption()` 関数は、Web ビューアでのみ有効になるので、Web アプリケーションから、FileMaker をデータソースとして使う場合は使用できないので、注意しましょう。あくまで、FileMaker レイアウトの代替として、Web ビューアを使う場合に使える関数です。

 この関数で起動する FileMaker スクリプトを作ります。
```.calc:StoreWebData
ユーザによる強制終了を許可 [ オフ ]
# 
変数を設定 [ $json ; 値: Get ( スクリプト引数 ) ]
# 
フィールド設定 [ SimpleForm::itemName ; JSONGetElement ( $json ; "itemName" ) ]
フィールド設定 [ SimpleForm::productCd ; JSONGetElement ( $json ; "productCd" ) ]
フィールド設定 [ SimpleForm::quantity ; JSONGetElement ( $json ; "quantity" ) ]
フィールド設定 [ SimpleForm::unitPrice ; JSONGetElement ( $json ; "unitPrice" ) ]
フィールド設定 [ SimpleForm::amount ; JSONGetElement ( $json ; "amount" ) ]
# 
レコード/検索条件確定 [ ダイアログあり: オフ ]

```

 これで Web フォームから、JavaScript 関数、そして FileMaker スクリプトという流れで、localhost 側のデータ更新を FileMaker に反映することができるようになりました。

![スクリーンショット 2025-03-06 20.13.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/09347b7f-e77f-4a84-ab2d-c95d13624cd3.png)

 目的のレイアウトが完成したら、FileMaker フィールドを不可視にして、Web ビューアの自動サイズ調整の上下左右をロックすれば、レスポンシブデザインのレイアウトが完成します。
![スクリーンショット 2025-03-06 20.15.28.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/c410218c-aab1-46ce-a77c-695c360b5d4c.png)
![スクリーンショット 2025-03-06 20.16.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3833520/4510d81c-dd21-419a-90aa-aac8dfb24efa.png)

## まとめ

### FileMaker Pro から localhost
1. フィールド内容を取得して、`Web ビューアで JavaScript を実行` スクリプトステップを実行する。このとき、フィールド内容を引数にする
1. `Web ビューアで JavaScript を実行` スクリプトステップによって渡される引数を処理して、Web フォームに反映させる

### localhost から FileMaker Pro
1. サブミットされたとき、Web フォームのデータを読み取り、引数にして、`FileMaker.PerformScriptWithOption()` 関数を実行し、FileMaker スクリプトを起動する
1. 起動された FileMaker スクリプトで引数を受け取り、FileMaker フィールドにセットする

 以上が基本的な流れです。これに、フィールドの入力チェックや自動計算等を加えて UI/UX として洗練させればいいと思います。どちらの方向でもデータは、一度、JavaScript に渡るわけですから、JavaScript 内で、データを処理を加えることもできますし、逆も真で、Web 側データを FileMaker に渡った段階で処理することも可能になります。

 例えば、まったく別の Web アプリケーションを localhost で作り、そこで作られたデータを、FileMaker に反映させることも可能になります。
 FileMaker Server も使えない、ODBC/JDBC も使えないという条件でも、アイディア次第で、色々なことができると思います。

 　次回からは、localhost 側のページ遷移を FileMaker Pro に反映させるということに取り掛かりたいと思います。