material-tableのセル内でデータごとの値を使用したURLリンクをレンダリングしたい
こんにちは、CX事業本部の若槻です。
今回は、前回の記事で作成したReact + Material-UI + material-tableのアプリで、テーブルのセル内でデータごとの値を使用したURLリンクをレンダリングしてみました。
実現したいこと
各商品データごとに予約数
が1
以上ならその商品に対する予約一覧ページへのURLリンクを予約数
列のセル内でレンダリングしたいです。
例えば、商品IDA0001
の「ペンライトセット」なら/reserve/productId=A0001
へのリンク、商品IDC0001
の「タオル」なら/reserve/productId=C0001
へのリンクを貼り、予約なしの「パンフレット」ならリンク無し、のようにしたいです。
やってみる
src/components/pages/ProductPage.tsx
のコードを次のように変更します。
ハイライトされた行が前回との変更(行追加)部分となります。それぞれ解説していきます。
import React from 'react'; import MaterialTable from 'material-table'; import GenericTemplate from '../templates/GenericTemplate'; import { ArrowUpward } from '@material-ui/icons'; import * as colors from '@material-ui/core/colors'; import SelectList from '../atoms/SelectList'; import moment from 'moment'; import { Link } from 'react-router-dom'; const ProductPage: React.FC = () => { interface Product { itemName: string; price: number; reserveDuedate: string; reserveCount: number; productId: string; } return ( <GenericTemplate title={'商品ページ'}> <MaterialTable icons={{ SortArrow: React.forwardRef((props, ref) => ( <ArrowUpward {...props} ref={ref} style={{ color: colors.blue[800] }} /> )), }} columns={[ { title: '商品名', field: 'itemName', defaultSort: 'asc', filtering: false, }, { title: '予約期限', field: 'reserveDuedate', filterComponent: (props) => ( <SelectList columnDef={props.columnDef} onFilterChanged={props.onFilterChanged} items={[ ['all', 'すべて'], ['notOverDue', '未超過'], ['overDue', '超過'], ]} /> ), customFilterAndSearch: (filterValue: string, rowData: Product) => { const jstNow = new Date().toLocaleString('ja', {}); if (filterValue === 'notOverDue') { return moment(rowData.reserveDuedate).isSameOrAfter(jstNow); } else if (filterValue === 'overDue') { return moment(rowData.reserveDuedate).isBefore(jstNow); } return true; }, }, { title: '価格', field: 'price', type: 'numeric', filtering: false, }, { title: '予約数', field: 'reserveCount', type: 'numeric', filterCellStyle: { textAlign: 'right' }, filterComponent: (props) => ( <SelectList columnDef={props.columnDef} onFilterChanged={props.onFilterChanged} items={[ ['all', 'すべて'], ['reserved', '予約あり'], ['notReserved', '予約なし'], ]} /> ), customFilterAndSearch: (filterValue: string, rowData: Product) => { if (filterValue === 'reserved') { return rowData.reserveCount > 0; } else if (filterValue === 'notReserved') { return rowData.reserveCount === 0; } return true; }, render: (rowData: Product) => { if (rowData.reserveCount) { return ( <Link to={{ pathname: '/reserve', search: `?productId=${rowData.productId}`, }} > {rowData.reserveCount} </Link> ); } else { return rowData.reserveCount; } }, }, ]} data={[ { itemName: 'ペンライトセット', price: 20000, reserveDuedate: '2020/08/10', reserveCount: 20, productId: 'A0001', }, { itemName: 'パンフレット', price: 4000, reserveDuedate: '2020/09/15', reserveCount: 0, productId: 'B0001', }, { itemName: 'タオル', price: 3000, reserveDuedate: '2020/08/30', reserveCount: 5, productId: 'C0001', }, { itemName: 'Tシャツ', price: 4500, reserveDuedate: '2020/08/30', reserveCount: 10, productId: 'C0002', }, ]} options={{ showTitle: false, filtering: true, }} /> </GenericTemplate> ); }; export default ProductPage;
まず、Reactでは<Link>
というコンポーネントを使ってサイト内リンクへのナビゲーションを実装します。
<Link>
は次のようにしてインポートすれば使用できます。
import { Link } from 'react-router-dom';
そして、material-tableで列のレンダリングを任意の内容でオーバーライドしたい場合は、columns
のrender
プロパティを使います。またrender
にfunctionを指定すると第一引数にデータが渡されるので、次のように予約数(reserveCount
)に応じてレンダリング内容を変更できます。そして予約数が1
以上の場合に<Link>
コンポーネントによりURL/reserve/productId={productId}
へのリンクをセル内でレンダリングするようにしています。
render: (rowData: Product) => { if (rowData.reserveCount) { return ( <Link to={{ pathname: '/reserve', search: `?productId=${rowData.productId}`, }} > {rowData.reserveCount} </Link> ); } else { return rowData.reserveCount; } },
予約数列に各商品ごとの予約へのリンクを貼ることができました。
リンクをクリックすると/reserve/productId={productId}
へちゃんと飛べました。(/reserve
ページは未実装なので画面には何も表示されていません。)
外部へのリンクの場合
<Link>
コンポーネントはサイト内へのナビゲーションリンクに使用します。外部へのリンクを貼りたい場合は次のように<a>
タグを使えばOKです。
render: () => { return <a href="https://classmethod.jp/">クラスメソッド</a>; },
リンクをクリックすると外部のページが開けました。
おわりに
React + Material-UI + material-tableのアプリで、テーブルのセル内でデータごとの値を使用したURLリンクをレンダリングしてみました。
本来は商品詳細ページを設けてそのページ内で予約一覧へのリンクを提供するべきかも知れませんが、今回は商品一覧ページのみで実現する方法を考えてみました。
参考
<Link>
| REACT TRAINING / REACT ROUTER- Custom Column Rendering | material-table
- How to display html link inside table cell using reactjs material-table | stackoverflow
- React Router Error related to Link To | stackoverflow
- サイト内リンクと外部リンクの設定 | GRAY CODE
以上