こんにちはジョージです。この前の iOS ハンズオンセミナーでご質問があった、ドラッグ & ドロップ でレコードの並び替えをやってみるよ。

並び替えのアプローチとゴールを検討
忘れた頃にやってくる、ドラッグ & ドロップ でレコードやポータルを並び替えたいという衝動。2026 年の今、どんなアプローチがあるか少し考えてみました。今までの経験を踏まえ、なんとなく以下のゴールを設定しました。
- 少なくとも 100 レコードの並び替えに対応
- リスト形式、ポータルのどちらにも対応
- 任意のレコード見出し、レコード画像を表示可能
- 余計な計算フィールドや集計フィールドは極力使わない
レコード一覧へ移動(レコード ID を指定)
注目するのはこちらのスクリプトステップ「レコード一覧へ移動(起点バージョン 22.0)」です。レコード ID を指定することで対象レコードの並び順を任意に指定することが可能となります。ドラッグ & ドロップ の操作を前提とすると並び替え自体は WebViewer を利用した方が簡単そうなのですが、カスタム App 自体のレコードのソートはこちらが便利です。
対象レコードのリストを作成
ドラッグ & ドロップは WebViewer で実行するとして、WebViewer に表示するリストはカスタム App で事前に作成しないといけません。この「余計な計算フィールドや集計フィールドは極力使わない」という制約を守る為に、いくつかの方法を検討しました。
- ExecuteSQL : WHERE … IN とGetRecordIDsFromFoundSet を組み合わせる事で、一番かっこよく必要な情報を取得できそうでしたが「現在のソート順」を維持するのが面倒くさいのでボツ。
- FileMaker Data API を実行: 上に同じ。「現在のテーブル」ポータルは空欄が帰ってくる?のでこれもボツ。
- 集計フィールド(一覧): 余計なフィールドが増えるのでボツ。
- 現在のレコードをループで回す: なんとなくレコード移動するのがいやなのでボツ。
「現在のテーブル ポータル」をループで回すのが一番しっくりきました。検索結果や、並び替えにも対応しているので必要な場が一番取得しやすかったです。ただし、レコード ID ポータルに配置した「レイアウト計算」では取得できなかったので、何かしらフィールドに保存する必要がありました。
# #
# 配列を初期化
変数を設定 [ $list ; 値: "[]" ]
#
# 並び替え配列を作成
変数を設定 [ $i ; 値: 1 ]
Loop [ フラッシュ: 常に ]
Exit Loop If [ $i > Get(対象レコード数) ]
変数を設定 [ $list ; 値: JSONSetElement ( $list; [ "[+]"; JSONSetElement ( "{}"; [ "recordId"; GetLayoutObjectAttribute ( "recordId" ; "content"; 1; $i ); JSONString ]; [ "label"; GetLayoutObjectAttribute ( "label" ; "content"; 1; $i ); JSONString ]; [ "thumb"; GetLayout… ]
変数を設定 [ $i ; 値: $i +1 ]
End Loop
#
# 並び替え配列を表示
// テキストを挿入 [ 選択 ; ターゲット: $list ; 「[ { "recid": "101", "label": "外部データ A" }, { "recid": "102", "label": "外部データ B" }, { "recid": "103", "label": "外部データ C" } ]」 ]
Web ビューアで JavaScript を実行 [ オブジェクト名: "wv" ; 関数名: "setOrder" ; 引数: $list ]
#
#
# 並び替えに使う JavaScript ライブラリ
デモを試したり、オプションや機能で比較した結果、SortableJS が使いやすそうでした。
基本的には setOrder, getOrder を定義して「WebViewer で JavaScript を実行」スクリプトステップ経由で、並び替えしたい JSON 配列を渡します。リストのラベルとサムネイル画像 ( JPEG/PNG ) は任意の内容を表示できるように recordId とは別に指定できるようにしてます。ライブラリのサンプルそのままの基本的な使い方です。
<script setup>
import { onMounted, onUnmounted, ref } from 'vue'
import Sortable from 'sortablejs'
const itemsRef = ref(null)
let sortable = null
const items = ref([])
onMounted(() => {
if (itemsRef.value) {
sortable = Sortable.create(itemsRef.value, {
animation: 150,
ghostClass: 'blue-background-class'
})
}
window.setOrder = (json) => {
let data = json
if (typeof json === 'string') {
try {
data = JSON.parse(json)
} catch (e) {
console.error(e)
return
}
}
if (Array.isArray(data)) {
items.value = data.map((item) => {
let thumb = item.thumb
if (thumb && typeof thumb === 'string' && !thumb.startsWith('data:')) {
thumb = thumb.startsWith('/9j/')
? `data:image/jpeg;base64,${thumb}`
: `data:image/png;base64,${thumb}`
}
return {
recordId: String(item.recordId),
label: item.label,
thumb: thumb
}
})
}
}
})
const getOrder = () => {
if (sortable) {
// alert(sortable.toArray())
exec("getOrder", sortable.toArray())
}
}
const exec = (s, p) => {
if(window.FileMaker != null) {
window.FileMaker.PerformScriptWithOption(s, p, 0);
} else {
setTimeout(exec, 1000, s, p); // 1s = 1000ms
};
}
onUnmounted(() => {
delete window.setOrder
})
</script>
<template>
<button @click="getOrder">並び順を取得</button>
<ul id="items" ref="itemsRef">
<li v-for="item in items" :key="item.recordId" :data-id="item.recordId">
<img v-if="item.thumb" :src="item.thumb" class="thumb" />
{{ item.label }}
</li>
</ul>
</template>
<style scoped>
ul {
list-style: none;
padding: 0;
}
li {
border: 1px solid #ccc;
padding: 10px;
/* margin-bottom: 5px; */
background-color: #f9f9f9;
display: flex;
align-items: center;
}
.blue-background-class {
opacity: .5;
background: #C8EBFB;
}
.thumb {
width: 50px;
margin-right: 10px;
}
</style>
カスタム App で並び替えの実行
「並び順を取得ボタン」をクリックしたら、並び替え後の recordId のセットを引数にして FileMaker のスクリプトを実行します。recordId を JSON 配列に変換し「レコード一覧へ移動」スクリプトステップを実行して並び替えをすれば完成です。
# #
# 配列を初期化
変数を設定 [ $param ; 値: Get ( スクリプト引数 ) ]
#
# WebViewer をリセット
Web ビューアの設定 [ オブジェクト名: "wv" ; 処理: リセット ]
#
# 並び替え実行
レコード一覧へ移動 [ レコード ID の一覧: JSONMakeArray ( $param ; Char(44) ; JSONNumber ) ; 使用するレイアウト: <現在のレイアウト> ; アニメーション: なし ]
#
# 恒久的に並びを保存したければ、ソート順フィールド等に現在の並びを保存
レコード/検索条件/ページへ移動 [ 最初の ]
#
#
#実際のレコードで検証
完成したので実際のレコードを使って並び替えをしてみます。総レコード数 5,726 件、画像 1 枚あたりの容量は 20KB から 50 KB ほどです。

- 10 件で並び替え: WebViewer の描画、並び替え後のカスタム App のソート、全て問題なし
- 50 件で並び替え: WebViewer の描画、並び替え後のカスタム App のソート、全て問題なし
- 100 件で並び替え: WebViewer の描画、並び替え後のカスタム App のソート、全て問題なし
現在のレコードセットの作成、WebViewer の描画、並び替え後のカスタム App 特にもたつきはありませんでした。とりあえず目標は達成です。
- 1000 件で並び替え: WebViewer の描画まで 34 秒、ドラッグ & ドロップ、並び替え後のカスタム App のソートは一瞬で完了し問題ない感じです。
- 2000 件で並び替え: WebViewer の描画まで 54 秒弱くらい、ドラッグ & ドロップ、並び替え後のカスタム App のソートは一瞬で完了し問題ない感じです。
- 3000 件で並び替え: WebViewer の描画まで 82 秒くらい、ドラッグ & ドロップは若干もたつくが許容範囲、並び替え後のカスタム App のソートは一瞬で完了し問題ない感じでした。
いずれもサムネイル画像をつくるのにもたついてますが(サムネイル画像つくらなければこんなに時間がかからない)、1000 件単位でドラッグ & ドロップしてレコードを並び替えるケースはあまりないと思いますので、とりあえず OK ではないでしょうか。
- 少なくとも 100 レコードの並び替えに対応 → OK
- リスト形式、ポータルのどちらにも対応 → 現在のテーブル or 関連レコードのポータル で動くはず
- 任意のレコード見出し、レコード画像を表示可能 → OK
- 余計な計算フィールドや集計フィールドは極力使わに → レコードIDだけ使ったが概ね OK
というわけで、かっこいい ドラッグ & ドロップ でレコードの並び替え ’26 でした。
