FileMakerでファイルを作るときに使い回す定番の小ネタ・小技があります。今回はソートボタンのスクリプト。
FileMakerはときどき「えっ。なんでこれができないの?」っていう機能の欠落(と感じること)があります。ソートの関数が用意されていないこともその一つ。
計算式でソートを制御することができません。ソート[ mainTable::ID ; DESC ] みたいな関数はありませんし、ソート項目を変数で切り替えるスクリプトも作れません。ソートはすべてソートダイアログで個別具体的に指示し保存するしかありません。これが不自由でみんな困ってます。多分困ってますよね?困ってませんか?そうですか。
ということでソートのスクリプトについてです。最初は個別にソートのスクリプトを作りまくる方法、下方にはスクリプト一個で済む代わりにフィールドを2個追加する二つの方法を記しました。
このページ内の見出し
その1 個別にソートスクリプトを作りまくる方法
何がしたいかというとリストのタイトル部分をクリックしたらソートされるようにしたいだけです。大概のリスト表示があるプログラムでよく見かけます。クリックでソートするだけでなくトグルで昇順降順が入れ替わります。
わざわざムービーにする必要もないけど。
このボタンスクリプト自体は汎用的に作れますが、肝心のどのフィールドをソートするのかという部分を全て個別にスクリプトを作っていきます。
(次の章では個別に作らずその代わりフィールドを二個用意する方法が書かれていますので、興味がある方はここをすっ飛ばして第二章にどうぞ)
動作の流れ
流れというか仕組みというか、必要な手続きは以下です。
- ソートのスクリプトを個別に必要なだけ作成します。
- 汎用的なソートスクリプトを作成します。
- レイアウト上でボタンを作って割り当てます。
★ 個別ソートスクリプトの名前の付け方にルールを設けます。
★ 個別ソートスクリプトの作り方にもルールがあります。
★ボタンにルールに則ったスクリプト引数をセットします。
ルール
ルールそのものが重要ってわけではなく、ルールに厳格なことが重要ってだけです。ですので決まり事は自分で好きなように作って良いわけですがそれだと記事にならないので「わたしはこうしたよ」というレベルにてお話を続けます。
個別ソートスクリプトの命名のルール
個別にせこせこと作るソートスクリプトです。スクリプト名の付け方に決まりを設けます。
ラベル+セパレータ+ソート順
ここではこうです。
ラベル
ソートスクリプトを作るときに付けたくなる普通の名前です。これが「ラベル」部分です。ラベルにルールはありません。ただし、次の「セパレータ」を含むことだけは避けます。
セパレータ
セパレータなしでもいいんですがここではセパレータがあるということで話を進めます。セパレータは「 – 」「 : 」「 _ 」「 | 」などです。ラベルに含まれる可能性がある文字は避けます。何にせよ、セパレータを決めてそれを守り抜きます。
ソート順
個別ソートスクリプトは必ず二個セットで作ります。昇順と降順の二つです。命名の最後はソート順がどっちなのかを示す言葉をくっつけます。昇順、降順でもいいしASC、DESCでも↑↓でもいいでしょう。それを決めて、必ず付けます。
てなわけで個別スクリプトの命名ルールでした。例えば こんな感じです。
スクリプト引数
ボタンにソートスクリプトを割り当てるとき、スクリプト引数をセットします。そこには「ラベル」を書くのです。IDなら「ID」です。スクリプト名の、セパレータより前の部分です。
個別ソートスクリプト作成のルール
個別のソートスクリプトの内容についてです。これにもルールがあります。
命名でも書いた通り必ず二個セットで作ります。同じソートの昇順と降順を作ります。
個別ソートのスクリプトの中身はスクリプトステップが2行。
1行目はスクリプト名に相応しいソートを「レコードのソート」ステップのダイアログで作って保存します。
2行目はグローバル変数をセットします。ここでは「$$sort」という名前にしておきましょう。$$sort に値をセットしますが、書き方にまたしてもルールがあります。
変数を設定 [ $$sort = Get ( スクリプト名 ) ]
こうです。
「ファイル名-ASC」という名前のスクリプトを実行させると、ファイル名で昇順ソートして、グローバル変数$$sortに「ファイル名-ASC」と記録します。これが個別ソートスクリプトの全貌です。
実際に作成していくには、スクリプトを複製して名前とソート設定だけ変えてやるというルーティンになりますね。
ソートボタンスクリプト
さて肝心のソートスクリプト行きます。ソートボタンスクリプトはすべてのソートボタンにセットする汎用的なソートスクリプトです。このスクリプトに書くステップを順に書きます。
まず変数を設定します。
変数を設定 [ $sep ; 値: "-" ]
命名ルールで定めたセパレータを変数に保存しました。念のための措置。
変数を設定 [ $label ; Get(スクリプト引数) ]
次にスクリプト引数を取得しました。ルール上、それは必ずラベルになっているはずですね。
次は条件分岐です。グローバル変数 $$sort の値の中に、ラベルがあるかどうかを調べて処理を分けます。
if ( PatternCount ( $$sort ; $label ) ) #あるとき〜 else #ないとき〜 end if
$$sort に自分と同じラベルが含まれていれば、次はソート順を見ます。
あるとき〜
$$sortには必ず ASC か DESC が入っているので、ASCなら逆であるDESCスクリプトを、DESC なら逆であるASCスクリプトを発動させます。
if [ patternCount ( $$sort ; "ASC" ) ] スクリプト実行 [名前で ; $label & $sep & "DESC" ] else スクリプト実行 [名前で ; $label & $sep & "ASC" ] end if
個別ソートのスクリプトを名前で実行することで、全てのソートボタン対応の汎用性を手に入れています。
ないとき〜
$$sort に自分と同じラベルがないときは未ソートであるからして、ASCかDESCのどちらか好きなほうをデフォルトとして発動させます。
ということで汎用ソートスクリプトは以上です。一応まとめときますか。
if ( PatternCount ( $$sort ; $label ) ) #あるとき〜 if [ patternCount ( $$sort ; "ASC" ) ] スクリプト実行 [名前で ; $label & $sep & "DESC" ] else スクリプト実行 [名前で ; $label & $sep & "ASC" ] end if else #ないとき〜 スクリプト実行 [名前で ; $label & $sep & "ASC" ] end if
たったこれだけのシンプルな話でした。
レイアウトでボタンを使う
ソートボタンスクリプトは書いた。はい。個別ソートのスクリプトも作った。はい。あとはレイアウトで実装するだけ。ボタンにソートボタンスクリプトの実行をセットして引数を書きます。
リスト的な表示(リストやポータル)のヘッダにタイトルのラベルを置きたくなります。このラベルをボタンにするんです。
ボタンバー
ボタンバーを使うと計算でボタンラベルを変更できるから昇順降順を示せます($$sortに応じてテキストを変える)
ボタンラベルの計算式はこんな感じです。
Let ( [ ラベル = "作成日" ; label = If ( PatternCount ( $$sort ; "ASC" ) and PatternCount ( $$sort ; ラベル ) ; ラベル & " ▲" ; If ( PatternCount ( $$sort ; "DESC" ) and PatternCount ( $$sort ; ラベル ) ; ラベル & " ▼"; ラベル & " " ) ) ]; label )
最初の「ラベル = “”」にボタンスクリプトの Label を指定するだけで動作しますから使い回しも楽々です。
「ラベル」とスクリプト名を分けたいときは、表示用ラベルとラベルの二つの変数を用意しておくといいですね。
ソートその2:専用フィールドを作成してソートを自動化する
上記方法ではスクリプトワークスペースにスクリプトを沢山作らなければなりません。作りたくない場合、別の方法があります。ファイルメーカーのヘルプにも載っているやり方で、専用フィールドを作成する方法です。
こちらの方法は、個別ソートのスクリプトを作らなくても良い代わりに、ソート専用のフィールドを作っておく必要があります。
ヘルプに載ってるまともで簡素なやり方ですが、少し知恵が必要な部分もあります。
材料
まず、グローバルフィールドを一つ、計算フィールドを一つ追加します。
例としてグローバルフィールドを「GLBfieldName」、計算フィールドを「sortContent」と名付けてみます。
グローバルフィールド GLBfieldName は、ソートするターゲットになるフィールド名を記入するための入れ物です。
計算フィールド sortContent は、グローバルフィールドで指定したフィールドの内容をそっくりそのまま複製して格納する入れ物です。計算式の基本は GetField ( GLBfieldName ) です。GLBfieldName で指定したフィールドの内容がゲットできるという案配です。
あとはソートのスクリプトが一つだけあれば良いです。実際の運用にあたっては、ソートスクリプトを発動させるためのソートボタンのスクリプトをひとつ追加すれば良いでしょう。
理屈
GLBfieldName にソートしたいフィールド名を記入すると、sortContent にそのフィールドの内容が転記されます。フィールド sortContent をソートします。
計算フィールドの計算式
理屈の通り、sortContent フィールドにフィールド内容を計算して転記するわけですが、この計算フィールドを改良します。
基本、GetField ( GLBfieldName ) の一行でOKです。
この簡単な式でフィールド内容は転記されますが、ソートする際の障壁があります。
結果のタイプ
計算式では、計算結果のタイプを「テキスト」や「数字」など指定しなければなりません。でも実際にソートしたいフィールドの内容はテキストだったり数字だったりします。
計算式の結果を「テキスト」にすると、数字のソートが上手くいきません。「数字」にするとテキストの結果が上手い具合に返りません。
汎用的な計算式ですから、ここに工夫が必要になります。
FieldType
取りあえず計算結果は「テキスト」にしておくとして、指定するフィールドが数字や日付の場合に備えておけばいいでしょう。
備えるのは良いのですが、指定するフィールドがテキストなのか数字なのかをまず調査しなければなりません。大丈夫。FieldType という関数があります。
FieldType ( ファイル名 ; フィールド名)
これを使うと指定フィールドの情報が複数行で返ってきます。2行目がフィールドタイプです。ですので具体的に次のように書きます。
GetValue ( Substitute ( FieldType ( Get ( ファイル名 ) ; GLBfieldName ) ; 2 ) )
結果は「text」「Number」「Date」といった文字列になります。
数字フィールドだったら、無理矢理テキストとしてソートできる形にします。つまり桁数を増やします。
Right ( "0000000" & フィールド内容 ; 8 )
例えばこんな風に8桁にしてしまえば、テキストフィールドとしてソートしても期待通りの結果となるでしょう。桁数はご自由に。
日付フィールドの場合は、各々お好きに工夫します。私の場合は書式を揃えているのでそのままテキストとしてソートしても問題ありません。そうでない場合は、桁数が揃うように整形します。
Let 関数
ということで、sortContent フィールドの計算式は具体的こうなりました。
Let ( [ content = GetField ( GLB_sortFiled ) ; fieldtype = GetValue ( Substitute ( FieldType ( Get ( ファイル名 ) ; GLBfieldName ) ; 2 ) ) ; content = case ( fieldtype = "number" ; Right ( "0000000" & content ; 8 ) ; content ) ]; content )
GLB_sortFiled にフィールド名を記入することで、sortContent にはソートできる形でフィールド内容が転記されます。あとはスクリプトでソートするだけです。
ソートするスクリプト
ソートスクリプトは、計算フィールド sortContent フィールドをソートさせれば良いだけです。
ダイアログでsortContent フィールドを指定します。
しかしこれだけでは貧弱ですね。グローバル変数の作成なんかを交えつつ、次のようなスクリプトを作成しました。
ソートスクリプト
ソートを実行するとグローバル変数を作成し「フィールド名 ¶ ソート順」を記録します。
ソートを実行する際、まずグローバル変数を見ます。現在すでに同じフィールド名でソートされているかどうかで分岐し、されていればソート順を見て現在とは異なる順でソートします。されていなければデフォルトのソートを行います。
ソートボタン
GLB_sortFiled にフィールド名を記入すればそのフィールドでソートされるようになりました。実際の運用ではフィールド名を手入力などしません。GLB_sortFiled にフィールド名を記入するボタンを作りましょう。
ソートボタンに割り当てるスクリプトを追加しました。
ボタンの設定で、スクリプト引数にフィールド名をセットします。この引数がグローバルフィールドにそのまま入る仕組みです。あとはソートスクリプトを実行させます。
普通のボタンじゃなくボタンバーを使うことによってボタンタイトルを計算式で作れます。上の「レイアウトでボタンを使う」に書いたとおりです。
ということで、別の方法を追記しました。こっちの方法が簡単で良いかもしれないですね。このためだけにフィールドを二つ追加することを許容できるかどうか、それは各々のご判断。私、以前はソートのためだけにフィールドを追加することに抵抗があったんですが近頃は何の抵抗も感じなくなってきて、こちらの方法を使うことが増えました。