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
以上