Tableの利用

Tableコンポーネントを使用してデータを操作する

このページで行うこと

大部分の内部ツールでは、自分のデータを閲覧したり操作したりすることができます。Table (テーブル)やいくつかの周辺コンポーネントを利用してRetool内でデータを閲覧および操作する方法を見ていきましょう。ここで行う操作の概要は次のとおりです。

  1. ユーザーのデータをテーブルに表示させる
  2. テーブルのフィルタを追加する
  3. 検索フィールドを追加する
  4. テーブルの値を編集する

シンプルなテーブル設定を構築した後に、テーブルのより高度なプロパティと機能をいくつか見ていきます。

それでは、やってみましょう。

テーブルへのデータの取込み

Retoolでアプリを新規作成すると、空白のキャンバスが表示されます。新しいコンポーネントを追加するには、画面右側のサイドバーからコンポーネントをドラッグします(表示を切り替えるには、メニュー・バーにある右側サイドバーのアイコンをクリックします)。最初に、アプリのキャンバスにTableコンポーネントをドラッグします。画面右側のサイドバーにTableコンポーネントがない場合、「Search components」バーで検索することができます。

アプリにクエリーがない場合は、先ほど追加したTableにサンプル・データが取り込まれます。Tableコンポーネントをクリックすると、画面右側のサイドバーにそのプロパティが表示されます。「data」プロパティの表示は次のようになります。

[{
  "id": 1,
  "name": "Hanson Deck",
  "email": "[email protected]",
  "sales": 37
}, {
  "id": 2,
  "name": "Max Conversation",
  "email": "[email protected]",
  "sales": 424
}, {
  "id": 3,
  "name": "Jason Response",
  "email": "[email protected]",
  "sales": 55
}, {
  "id": 4,
  "name": "Sue Shei",
  "email": "[email protected]",
  "sales": 550
}, {
  "id": 5,
  "name": "Eric Widget",
  "email": "[email protected]",
  "sales": 243
}]

これらの人たちでも構いませんが、自分のデータに置き換えてみましょう。このテーブルに自分の情報を取り込むために、クエリーの構築を行う必要があります。Retoolでは、ボトム・バーでクエリーを記述します。ボトム・バーが表示されていない場合は、メニュー・バーにあるボトム・バーのアイコンをクリックして表示を切り替えます。

既に作成したクエリー(query1)が表示されるはずです。表示されない場合は、右側の「New Query」ボタンをクリックしてクエリーを新規作成します。

既にデータ・ソースをRetoolに接続している場合は、クエリー・バーのResourceドロップダウンからそのデータ・ソースを選択します。データ・ソースに接続していない場合は、Retoolのドキュメントを確認し、デフォルトのサンプル・リソースの1つを使用してください。ここでは、構築したデータベース(名称: ecommerce)を使用しましょう。画面の表示は次のようになります。

ユーザー・テーブルからユーザーのデータを取り込むためのシンプルなクエリーをクエリー・エディター内に書き込みます。

select *
from users

Preview (またはCommand+Enter)をクリックしてクエリーを実行し、結果を確認します。その後、Save (またはCommand+S)をクリックしてクエリーを保存します。クエリー名をクリックして新たな名前を入力し、クエリーを使いやすくします。例えば次のような名前に変更しましょう: users.クエリーが完了すると、次のようになります。

テーブルを見ると、すべてのサンプル・データのみが表示されており、何も変更されていないことに気づくでしょう。これはクエリーで取り込んだデータをテーブルに紐付ける必要があるためです。そのため、Tableコンポーネントをクリックして画面右側のサイドバーを表示させ、サンプル・データ(dataプロパティ)をクエリーへの参照情報に置き換えます。

クエリーとは別に、Retool内のすべてのコードは二重の角括弧内に記述されています。そのため、クエリー・データをこのテーブル内で参照するために、サンプル・データを{{ users.data }}に置き換えました。作成したクエリーの名前をusersにしたことを思い出してください。現在、.dataプロパティを使用してそのクエリー結果を参照しています。

テーブル・フィルタの追加

RetoolのTableには事前にフィルタリング機能が備わっています。そのため、カスタムSQLやJavaScriptを記述する必要はありません。テーブルの左下にある小さなフィルター・アイコンをクリックすると、フィルタ・オプションが表示されます。

テーブルのデータ内のいずれかの列を選択し、containsequalsなどの演算子によりフィルタすることができます。また、andまたはorのロジックを使用して複数のフィルタを追加することもできます。テーブルおよびフィルタの使用に関する詳細については、こちらのテーブル・リファレンスを確認してください。

検索バーの追加

📘

ビデオ・ウォークスルー

テーブルへの検索機能の追加について簡単なビデオ・ウォークスルーを用意しましたこちら( English )。

検索機能の追加は少し複雑です。テキスト入力コンポーネントを追加し、usersクエリーのSQLを変更する必要があります。最初にテーブル上のキャンバスにテキスト入力コンポーネントをドラッグします。

また、クエリー内で使用できるように検索バーに名前を付けましょう。テキスト入力コンポーネントをクリックして、名前をsearch_barに変更します。

現時点で、この検索バーに入力しても実際は何も起こりません。クエリーを検索バーに結びつける必要があります。そのため、usersクエリーに新規行を追加して、検索バーに入力した内容が何であっても、ユーザーの名前をフィルタします。クエリーの.valueプロパティを利用して、検索バーへの入力内容を参照します。クエリー内でRetoolのコンポーネントを参照しているときに、Retoolでは上記のように二重角括弧の表記法を使用するため注意してください。

/* our original query */
select *
from users
/* our new filter for search */
where {{ !search_bar.value }}
or first_name || last_name ilike {{ '%' + search_bar.value + '%' }}

このwhere句には2つの構成要素があります。

  1. where {{ !search_bar.value }}は、検索バーに何も入力されていない場合に、search_barを無視するようクエリーに指示しています。
  2. or first_name || last_name ilike {{ '%' + search_bar.value + '%' }}では、first_namelast_nameを連結しており、検索バーに入力した内容のilikeを使用してフィルタします。

マジック演算子(%)は、氏名をあいまい検索するように、このクエリー言語に指示しています。クエリーを試行および保存すると、検索機能が利用できるようになります。

テーブルの編集(データを編集可能にする)

データを表示した後に、値を直接変更したい場合があります。ユーザーをテーブルに取り込むための簡単なツールを構築し、ユーザーの名前を変更するとしましょう。この機能を有効にするには、一括変更クエリーを作成します。 一括変更クエリーを使用すると、テーブル内で値を編集し、変更内容を保存することができます。このクエリーはどのようなものでも構いません。この例では、「Bulk Update via Primary Key」クエリーを使用します。

📘

テーブルのrecordUpdatesプロパティとは?

テーブルの.recordUpdatesプロパティは、行が変更されたことを知らせるオブジェクト配列になっています。例えば、1つの行のfirst_nameプロパティを変更した場合、その行のすべてのフィールドが値として返され、次のようになります:

[{ customer_id: 24, first_name: 'New Name', last_name: 'Same Name'}]

このクエリーを実行すると、テーブルにデータを取り込むクエリーが再実行されることも忘れないでください。これにより、テーブル内のデータが常に最新の状態であることが確実になります。クエリー・エディター内の「After this query runs」セクションで、このようなクエリーを実行することができます。

この設定の後に、ユーザーによる編集を可能にすべき列を選択しましょう。今回は、first_namelast_nameを編集可能にします。

これでツールが使えるようになりました。操作中の状況を分かりやすく示したGIFをご覧ください。

サーバー・サイドでのページング

多くの場合、一度に1ページ分のデータのみを表示したくなりますが、すべてのデータにアクセスするケースもあります。RetoolのTableはサーバー・サイドでのページングに対応しています。TableコンポーネントのAdvanced Settingsセクションで Server side paginated を有効にし、設定することができます。

「Server side paginated?」チェックボックスにチェックを入れ、2つのクエリーを作成します。「Server side paginated?」チェックボックスにチェックを入れ、2つのクエリーを作成します。

「Server side paginated?」チェックボックスにチェックを入れ、2つのクエリーを作成します。

query.dataはデータの読み出しに使用します。一方、queryCount.data.countは結果の合計件数を取得する別のクエリーになります。

今回のデータを読み出すクエリーは次のようになるでしょう。

select * from customer where first_name ilike {{ '%' + textinput1.value + '%' }}
limit {{ table1.pageSize }}
offset {{ table1.paginationOffset }}

このクエリーでは最後の2行が重要です。limit文とoffset文では正しい範囲の行を読み出すためにテーブルの値を使用しています。

結果の合計件数を算出するためのクエリーは下記のようになります。

select count(*) from customer where first_name ilike {{ '%' + textinput1.value + '%' }}

ここで重要なのは、2つのクエリーのフィルタ条件が同一であることです。これにより、行数を正確に算出することができます。上記の処理により、このテーブルは、ユーザーが選択しているページが変わったときに、正しいページのデータを必ず自動的に読み出すはずです。

列の動的な並べ替えを伴うサーバー・サイドでのページング

サーバー・サイドのページングとともにテーブルの列の並べ替えをフックするには、TableのsortedColumnプロパティとsortedDescプロパティを利用する必要があります。下記がその例になります。

select * from customer
order by

-- For each column you want to enable sorting by you must add the following two rows - including the ending comma.
case when {{ table1.sortedColumn == 'email' }} and {{ table1.sortedDesc }} then email end desc,
case when {{ table1.sortedColumn == 'email' }} and {{ !table1.sortedDesc }} then email end asc,

case when {{ table1.sortedColumn == 'first_name' }} and {{ table1.sortedDesc }} then first_name end desc,
case when {{ table1.sortedColumn == 'first_name' }} and {{ !table1.sortedDesc }} then first_name end asc

limit {{ table1.pageSize}}
offset {{ table1.paginationOffset }}

詳細な記述ですが、SQLクエリーをSQLインジェクションに耐えられるパラメーター化したクエリーに変換することになるため、これは必要になります。

制限 - オフセット・ベースのページング

これは、ページ番号の表示に対応し、レスポンス数を制限しているSQLデータベースおよびAPIとともに使用することが最適です。

1. クエリーを作成し、Tableにデータを表示させてから、サーバー・サイドにおけるページングを有効にします。

2. データをテーブルに読み出すクエリー内で.pageSize.paginationOffsetを使用します。

次の例を参照してください。

select * from customer
limit {{ table1.pageSize}}
offset {{ table1.paginationOffset}}

3. クエリー結果数を別途提供します。

閲覧したいページに飛ぶことができるようにテーブルを設定することも可能です。このような設定をするには、ページングの対象になるデータセット全体における結果の合計件数をTableに提供する必要があります。通常、下記のような個別のクエリーで実行できます。

select count(*) from customer

カーソル・ベースのページング

📘

カーソル・ベースのページングとは?

カーソル・ベースのページングの紹介は、Slackの素晴らしいブログへの投稿に記載されています。

Retoolでカーソル・ベースのページングを使用する方法を簡単に紹介します。この例では、カーソルを使用したページングを実装しているStripeのREST APIにTableを接続します。

1. StripeのREST APIリソースに接続します

最初に、Stripeに対して認証済みのAPIコールを行うことができるように、Stripe API REST APIリソースを作成します。次のスクリーンショットを入力例として使用してください。

2. TableにStripeの顧客を表示します

リソースの作成後に、Stripeに対してAPIコールを実行して顧客リストを取得し、TableにStripeの顧客を表示します。

🚧

これは何のための作業ですか?

何のためにこの作業をしているのかが分からない場合は、クイックスタートを確認してから、このページに戻ってきてください。

3. Tableのページング設定を行います

StripeのAPIは一度に最大で100件のレコードのみを返します。そのため、すべてのデータ・リストを参照できるようにする場合は、StripeのPagination APIを使用する必要があります。

TableのInspectorプロパティを下にスクロールします。Paginationセクション内で、Server-side paginationを有効にし、Pagination typeドロップダウンメニューからCursor basedを選択します。

次に、Next page cursorフィールド内で、次のページに進む際の基点を何にすべきかをTableに指定する必要があります。今回は、Table内の最後の顧客のidを基点とします。

4. クエリーをTableのページング設定に紐付けます

次に、Tableのページングプロパティを使用するために、APIクエリーを変更する必要があります。次のスクリーンショットを入力例として使用することができます。

📘

{{ table1.afterCursor ? 'starting_after' : null }}とはどういうことですか?

StripeのAPIでは、starting_afterパラメーターの引数にnullを指定することができません。TableのafterCursorプロパティがnullである場合、この問題を回避するために、{{ table1.afterCursor ? 'starting_after' : null }}という表現を用いてstarting_afterパラメーターを取り除きます。

その後、クエリーを保存すると、Tableのページングが正しくなるはずです。

ページングの動作

Table内の次のページに進むボタンをクリックすると、TableのafterCursorプロパティがNext page cursorプロパティで指定した値に変わります。これにより、APIクエリーが再実行されます。そして、新しいデータ一式がTableのデータに追加されます。Tableは以前APIから読み込んだ結果を保存しているため、APIコールを再実行することなくTable内の前のページに戻ることが可能になります。

GraphQLを用いたカーソル・ベースのページング

詳細については、https://facebook.github.io/relay/graphql/connections.htmを確認してください。

1. ページングに対応したパラメーターを含むGraphQLクエリーを作成します

Github GraphQL APIを使用してこのようなクエリを作成する方法の例は次のとおりです。クエリーを実行してもエラーが発生しないように、引数にプレースホルダーの値を入力しました。

2. Table内にデータを表示します

3. Table内でのサーバー・サイドのページングを有効にし、Pagination Typeでは「GraphQL Cursor based」を選択します

Pagination Typeを選択した後に、{{ }}を使用して前のページを読み出すための基点を記述し、次のページのデータを表示するための基点を別途記述します。

4. クエリー内のプレースホルダーの変数をTableのプロパティに置き換えます

TableでbeforeCursorプロパティおよびafterCursorプロパティが定義されているか否かに応じて、firstlastをどのように交互に使用しているかに注意してください。

5. クエリを試行し、その動作を理解します

2つのシナリオがあります。

A) ユーザーがTable上で次のページに進もうとしている。

この場合、Tableは、その時点の「Next Page Cursor」の値が何であっても、その値をafterCursorプロパティに設定します。同時に、Tableは、nullを「beforeCursor」プロパティに設定します。これにより、ページが次に進む時とページが前に戻る時を区別する際の曖昧さが解消されます。

B) ユーザーがTable上で前のページに進もうとしている。

例A)と同様に、Tableは、その時点のPrevious Page CursorbeforeCursorプロパティに設定した上で、nullafterCursorプロパティに設定します。

Tableの列の色付け

Tableコンポーネントを選択した後に、Inspectorプロパティ内のColumns設定を開くと、Background color設定が表示されます。ここでは{{ }}タグの内側でJavaScriptを使用でき、selfを用いることでセルの現在の色を参照することができます。一般的な色の名称や16進数コードを文字列として用いて、使用する色を定義することができます。

Tableの列の並べ替え

列を並べ替えるために、その列をクリックします。この操作では最大10,000行まで対応できますが、その後、クエリーで並べ替えが必要になる場合があります。

Table内の操作ボタン

各行にボタンを追加しますか?「Add action button」のチェックボックスにチェックを入れると、各行にボタンを配置できます。ボタンを押した時の動作に加えて、ボタンの文言もカスタマイズすることができます。

操作ボタンを押したときにクエリーを実行させる場合、マジック変数のiを定義します。この変数は選択した行のインデックスを示すものです。行のデータを参照するには、{{ table1.data[i] }}を使用します。SQLクエリーの場合には、これに代えて、{{ table1.data[i].columnName }}を使用します。今後、実際の行データを提供する予定です。

ユーザーのStripe残高をリセットする操作ボタンの追加ユーザーのStripe残高をリセットする操作ボタンの追加

ユーザーのStripe残高をリセットする操作ボタンの追加

クリップボードへのTableデータのコピー

Buttonコンポーネントを使用して、シンプルなJavaScriptを1行記述するだけで、Tableコンポーネントからデータをコピーすることができます。Tableの隣にButtonコンポーネントをドラッグします。そのコンポーネントをクリックすると画面右側にプロパティのサイドバーが表示されます。「On click」という見出しの下で、「Copy to clipboard」を選択します。

「Value to copy」フィールドに記述するJavaScriptは、Table内のデータの種類によって異なります。SQLデータベースを使用している場合は、{{ Papa.unparse(formatDataAsArray(table1.data), { delimiter: '\t' }) }}と記述します。JSONに基づくものを使用している場合は、{{ Papa.unparse(table1.data, { delimiter: '\t' }) }}と記述します。ボタンの名前を「クリップボードにコピー」に変更すれば完了です。

カスタム列/計算列

Retoolアプリ内の他の値に基づいて値を算出する計算列をTableに追加することができます。{{ currentRow }}を使用したTableまたはその他のコンポーネント/データ・ソース内で、このような列の値を他のデータに基づいて計算することができます。例えば、monthlyIncomeという列がある場合、yearlyIncome列の値を計算するために、{{ currentRow.monthlyIncome * 12 }}を使用します。

カスタム列には、次のものを含め、標準のTableの列と同一の属性があります。

  1. 列の種類
  2. カスタム見出し
  3. 背景の色
  4. HTML、オーバーフロー、並べ替え、表示/非表示の動的な切替え

📘

カスタム列の属性への参照

カスタム列の名前を変更することができますが、各列には変更できないkeyがあります。例えば、2番目のカスタム列の名前を"xyz"にした場合、マッパーを動的に参照するには、{{ table1.columnMappers['Calculated Column 2']}}を使用する必要があります。

🚧

他のコンポーネントにおけるカスタム列のデータの参照

現時点でTableオブジェクトの{{ table1.data }}または他のプロパティにおけるカスタム列のデータを参照することはできません。このような計算済の値を他のコンポーネントで参照する場合(例: {{ table1.selectedRow.data.custom_column }}の使用)、カスタム列を参照するのではなく、Query Transformerを使用して追加の値を計算することをお勧めします。これらの値は、その後Tableに渡ります。

Tableへの行の追加

簡単な方法でTableに新規行を追加したい場合、Tableコンポーネント上に小さな「+」ボタンを配置して、追加クエリーと結び付けます。詳細については、こちらの手順書を確認してください。概要は次のとおりです。

Tableコンポーネントをクリックして、画面右側にあるサイドバー内のInspectorを開きます。

下にスクロールすると「Table Edit Queries」セクションがあります。新規行が追加されたときに実行する追加クエリーをここで選択します。

追加クエリーには、Tableコンポーネントの.newRowプロパティを含める必要があります。

必要な作業がもう1つあります。ユーザーが入力した情報を編集可能にしたいTable内のすべての列を設定する必要があります。Inspector内の各列をクリックし、「Make Editable」をオンにすることで編集可能になります。

これで、「+」アイコンを使用してTableに行を追加できるようになるはずです。

❗️

「Make Editable」をオンにしたTableの列においてのみ新しい行のUIに入力することができ、値が入力されている列にのみ{{tableName.newRow}}の内側で指定したキーが設定されます。

Tableに複数の行を追加する際の動的な値の使用

複数の新規行を追加する際に、その行にユーザーが編集できない列がある場合、値が動的に定義されるようにすると役立つ場合があります。例としては、{{ moment() }}を用いることにより作成日を定義したり、行を作成したユーザーのEメール・アドレスを{{current_user.email}}を使用して自動的に入力したりすることが挙げられます。

Tableに複数の新規行を作成するときに、{{ }}で囲んだ値を挿入することができますTableに複数の新規行を作成するときに、{{ }}で囲んだ値を挿入することができます

Tableに複数の新規行を作成するときに、{{ }}で囲んだ値を挿入することができます

動的な列の設定

列の表示/非表示の動的な切替え

Tableの「Data」設定において入力したデータの値に適用する列設定の一部を動的に管理することができます。「Column settings」に入力する内容は、表示すべきすべての列のためのオブジェクト配列であることを想定しています。列名に合致するnameキーが配列内のどのオブジェクトにもない場合、その列はTable上に表示されません。

列の種類の動的な変更

動的な列の設定では、プログラムによって列の種類の操作が可能になります。「type」の有効な値は次のとおりです。

  • default
  • modal
  • button
  • checkbox
  • datepicker
  • textinput
  • dropdown
  • string
  • date
  • datetime
  • datetime_tz
  • link
  • object
  • integer
  • percent
  • float
  • usd_dollars
  • usd_cents
  • boolean

列マッパーの動的な設定

各列のオブジェクトの内側を文字列として使用するようにマッパーを設定することができますが、{{ }}タグはエスケープする必要があります。

`{ "name": "email", "type":"default", "mapper":'{\{self+1}\}'}`

🚧

動的なマッパーに関する注意事項

動的に作成されたマッパーはTableのデータまたはアプリが読み込まれるときにのみ更新されます。そのため、マッパーの値を編集した場合、マッパーを最新の状態にする前にデータを再度読み込む必要があります。

動的なマッパーを設定した後に、Column Settingsのマッパー・フィールドはグレーアウトされ、値はリアルタイムに更新されます。

列の背景色の動的な設定

「colorMapper」キーには「mapper」キーと同一のルールと注意事項が適用されますが、その値はBackground color設定で使用されます。文字列内で外側の引用符を繰り返し使用しないように、ここでは外側の引用符とは異なる一重引用符または二重引用符を使用する必要があります。
"colorMapper":'{\{Math.abs(i % 2) == 1?"grey":"blue"}\}'