회사에서 어드민 쪽 페이지를 만들게 되어서 여러가지 그리드 라이브러리들을 살펴봤다.
집중적으로 본 것은 mui data grid, react-table, ag grid가 있다.
난이도는 mui data grid <= ag grid <<<<<< react-table
mui data grid와 ag grid는 개발하기가 매우 쉽고 코드가 깔끔하다.
그 중에서 ag grid는 pivot 모드와 context모드 등등 advanced한 기능이 잘 되어있고
대용량 데이터를 처리하기에 적합하다.
그래서 ag grid가 채택되었고 스터디를 진행했다.
<포함된 내용>
1. 설치
2. 기본구조
3. editable cell/ row selection
4. 중첩 컬럼
5. 선택 한 값 가져오기
6. cell renderer
[설치]
npm install --save ag-grid-community ag-grid-react
처음에는 yarn add ag-grid-react 로 설치했었는데
이것만 설치하면 안되고 ag-grid-community도 같이 설치해야한다.
[기본구조]
import { AgGridReact, AgGridColumn } from "ag-grid-react";
AgGridReact 컴포넌트에 표에 들어갈 기본적인 옵션들과 데이터를 넣고
AgGridColumn 컴포넌트에 컬럼에 해당하는 옵션들과 값들을 넣어서 정의하는 것이다.
코드를 보면서 이해해보자.
[row selection/ editable cell]
//AgGrid.jsx
import React, { useState } from "react";
import { AgGridReact, AgGridColumn } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import "./styles.css";
import Button from "@mui/material/Button";
const AgGrid = () => {
const [gridApi, setGridApi] = useState(null);
const [gridColumnApi, setGridColumnApi] = useState(null);
const [rowData, setRowData] = useState(null);
const [selectedRows, setSelectedRows] = useState([]);
const [btndisabled, setBtnDisabled] = useState(true);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
const updateData = (data) => params.api.setRowData(data);
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((resp) => resp.json())
.then((data) => updateData(data));
};
const onSelectionChanged = () => {
const data = gridApi.getSelectedRows();
if (data.length > 0) {
setBtnDisabled(false);
} else {
setBtnDisabled(true);
}
setSelectedRows(gridApi.getSelectedRows());
};
const onCellValueChanged = (e) => {
console.log("changed", e.data);
};
return (
<>
<div style={{ width: "100%", height: "100%" }}>
<div
id="myGrid"
style={{
height: "600px",
width: "100%",
}}
className="ag-theme-alpine"
>
<h1>editable table</h1>
<div>
<Button variant="contained" disabled={btndisabled}>
action1
</Button>
<Button variant="contained" disabled={btndisabled}>
action1
</Button>
<Button variant="contained" disabled={btndisabled}>
action1
</Button>
</div>
<AgGridReact
rowData={rowData}
rowSelection={"multiple"}
suppressRowClickSelection={false}
defaultColDef={{
editable: true,
sortable: true,
minWidth: 100,
filter: true,
resizable: true,
floatingFilter: true,
flex: 1,
}}
sideBar={{
toolPanels: ["columns", "filters"],
defaultToolPanel: "",
}}
onGridReady={onGridReady}
onSelectionChanged={onSelectionChanged}
onCellEditingStopped={(e) => {
onCellValueChanged(e);
}}
>
<AgGridColumn
headerName="..HELLO."
headerCheckboxSelection={true}
checkboxSelection={true}
floatingFilter={false}
suppressMenu={true}
minWidth={50}
maxWidth={50}
width={50}
flex={0}
resizable={false}
sortable={false}
editable={false}
filter={false}
suppressColumnsToolPanel={true}
/>
<AgGridColumn headerName="Participant">
<AgGridColumn field="athlete" minWidth={170} />
<AgGridColumn field="country" minWidth={150} />
</AgGridColumn>
<AgGridColumn field="sport" />
<AgGridColumn headerName="Medals">
<AgGridColumn
field="total"
columnGroupShow="closed"
filter="agNumberColumnFilter"
width={120}
flex={0}
/>
<AgGridColumn
field="gold"
columnGroupShow="open"
filter="agNumberColumnFilter"
width={100}
flex={0}
/>
<AgGridColumn
field="silver"
columnGroupShow="open"
filter="agNumberColumnFilter"
width={100}
flex={0}
/>
<AgGridColumn
field="bronze"
columnGroupShow="open"
filter="agNumberColumnFilter"
width={100}
flex={0}
/>
</AgGridColumn>
<AgGridColumn field="year" filter="agNumberColumnFilter" />
</AgGridReact>
</div>
</div>
</>
);
};
export default AgGrid;
코드를 살펴보자
<AgGridReact
rowData={rowData}
rowSelection={"multiple"}
suppressRowClickSelection={false}
defaultColDef={{
editable: true,
sortable: true,
minWidth: 100,
filter: true,
resizable: true,
floatingFilter: true,
flex: 1,
}}
sideBar={{
toolPanels: ["columns", "filters"],
defaultToolPanel: "",
}}
onGridReady={onGridReady}
onSelectionChanged={onSelectionChanged}
onCellEditingStopped={(e) => {
onCellValueChanged(e);
}}
>
먼저 AgGridReact 컴포넌트로 컬럼 컴포넌트들을 감싸주면 된다.
rowData => data로 뿌려주고 싶은 값들이다.
key값과 AgGridColumn의 field에 있는 값이 같아야 해당 값이 해당 column에 나타난다.
여기서는 rowData를 useState로 선언하고
onGridReady 함수로 ag grid오픈 api에 fetch해서 가져오고 있다.
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
const updateData = (data) => params.api.setRowData(data);
fetch("https://www.ag-grid.com/example-assets/olympic-winners.json")
.then((resp) => resp.json())
.then((data) => updateData(data));
};
이 그리드를 생성할 때 onGridReady함수가 실행된다.
const onGridReady = (params) => {
console.log("params", params);
setGridApi(params.api);
setGridColumnApi(params.columnApi);
const updateData = (data) => params.api.setRowData(data);
axios
.get("http://localhost:3001/nosnos")
.then((res) => updateData(res.data));
};
axios를 사용하고 싶으면 fetch부분만 바꾸면 된다.
defaultColDef에 여러가지 옵션들을 지정해서 이 그리드가 어떤 기능을 가질 지 정의할 수 있다.
여기서는 editable, sortable, filter, resizable 등을 true로 해놓아서
cell 을 편집할 수 있고, column을 클릭하면 자동으로 sort가 되고 한다.
여러가지 props 들은 공식 페이지에서 확인 할 수 있다.
<AgGridColumn
headerName=""
headerCheckboxSelection={true}
checkboxSelection={true}
floatingFilter={false}
suppressMenu={true}
minWidth={50}
maxWidth={50}
width={50}
flex={0}
resizable={false}
sortable={false}
editable={false}
filter={false}
suppressColumnsToolPanel={true}
/>
row selection을 구현하고 싶다면 맨 첫번째 column을 checkbox 컬럼으로 만들면 된다.
이 컬럼은 다른 컬럼과 달리 field값이 없고 checkbox에 관련된 props들이 있다.
컬럼 자체가 checkbox여서 전체 선택이 가능하다.
[중첩 컬럼]
메달 컬럼은 펼칠 수 있을 것만 같이 생겼다.
펼치면 이렇게 아까는 없었던 Gold, Silver, Bronze 컬럼이 나온다.
<AgGridColumn headerName="Medals">
<AgGridColumn
field="total"
columnGroupShow="closed"
filter="agNumberColumnFilter"
width={120}
flex={0}
/>
<AgGridColumn
field="gold"
columnGroupShow="open"
filter="agNumberColumnFilter"
width={100}
flex={0}
/>
<AgGridColumn
field="silver"
columnGroupShow="open"
filter="agNumberColumnFilter"
width={100}
flex={0}
/>
<AgGridColumn
field="bronze"
columnGroupShow="open"
filter="agNumberColumnFilter"
width={100}
flex={0}
/>
</AgGridColumn>
이렇게 컬럼 안에 컬럼을 쓰면 된다.
columnGroupShow 라는 속성이 있는데 total은 이 그룹이 닫혀있을때 보여주겠다는 것이고
그 외는 open 되었을 때 보여주겠다는 속성이 들어있다.
[선택 한 값 가져오기]
추가로 나는 선택 한 row가 있을 때만 위의 button을 disabled = false로 바꾸고 싶었다.
그럴려면 row를 선택했는지 안했는지 여부와, 선택된 값을 가져와야 한다.
<AgGridReact
...
onSelectionChanged={onSelectionChanged}
...
>
그럴려면 onSelectionChanged 함수를 사용하면 된다.
selection이 바뀌었을 때 실행된다.
선택 된 data 는 gridApi.getSelectedRows() 로 접근할 수 있다.
[cell renderer]
동적으로 cell을 렌더링해서 가져오는 방법이다.
import React, { forwardRef, useImperativeHandle, useState } from "react";
import { render } from "react-dom";
import { AgGridReact, AgGridColumn } from "ag-grid-react";
// import "ag-grid-community/dist/styles/ag-grid.css";
// import "ag-grid-community/dist/styles/ag-theme-alpine.css";
const MoodRenderer = forwardRef((props, ref) => {
const imageForMood = (mood) =>
"https://www.ag-grid.com/example-assets/smileys/" +
(mood === "Happy" ? "happy.png" : "sad.png");
const [mood, setMood] = useState(imageForMood(props.value));
useImperativeHandle(ref, () => {
return {
refresh(params) {
setMood(imageForMood(params.value));
},
};
});
return <img width="20px" src={mood} alt="" />;
});
const GenderRenderer = (props) => {
const image = props.value === "Male" ? "male.png" : "female.png";
const imageSource = `https://www.ag-grid.com/example-assets/genders/${image}`;
return (
<span>
<img src={imageSource} alt="" />
{props.value}
</span>
);
};
const AgeRenderer = (props) => {
const age = props.value > 19 ? "adult🍕" : "child🍗";
return <span>{age}</span>;
};
const MyRenderer = (props) => {
return <span>{props.value} 입니다.</span>;
};
const CellRendering = () => {
const [gridApi, setGridApi] = useState(null);
const [gridColumnApi, setGridColumnApi] = useState(null);
const onGridReady = (params) => {
setGridApi(params.api);
setGridColumnApi(params.columnApi);
};
return (
<div style={{ width: "100%", height: "500px" }}>
<h2>cell render</h2>
<div
id="myGrid"
style={{
height: "100%",
width: "100%",
}}
className="ag-theme-alpine"
>
<AgGridReact
rowData={[
{
value: 14,
type: "age",
},
{
value: "female",
type: "gender",
},
{
value: "Happy",
type: "mood",
},
{
value: 21,
type: "age",
},
{
value: "male",
type: "gender",
},
{
value: "Sad",
type: "mood",
},
]}
defaultColDef={{ flex: 1 }}
frameworkComponents={{
genderCellRenderer: GenderRenderer,
moodCellRenderer: MoodRenderer,
myCellRenderer: MyRenderer,
ageCellRenderer: AgeRenderer,
}}
// onGridReady={onGridReady}
>
<AgGridColumn field="value" />
<AgGridColumn
headerName="Rendered Value"
field="value"
cellRendererSelector={(params) => {
const moodDetails = { component: "moodCellRenderer" };
const genderDetails = {
component: "genderCellRenderer",
params: {
values: ["Male", "Female"],
},
};
if (params.data.type === "gender") return genderDetails;
else if (params.data.type === "mood") return moodDetails;
else return undefined;
}}
/>
<AgGridColumn field="type" />
<AgGridColumn
headerName="my render"
field="value"
cellRendererSelector={(params) => {
const moodDetails = { component: "myCellRenderer" };
const genderDetails = {
component: "myCellRenderer",
params: {
values: ["Male", "Femail"],
},
};
const ageDetails = {
component: "ageCellRenderer",
};
if (params.data.type === "gender") return genderDetails;
else if (params.data.type === "mood") return moodDetails;
else if (params.data.type === "age") return ageDetails;
else return undefined;
}}
/>
</AgGridReact>
</div>
</div>
);
};
export default CellRendering;
cell에 다른 컴포넌트를 넣겠다는 의미이니까 다른 컴포넌트를 정의해준다.
const MoodRenderer = forwardRef((props, ref) => {
const imageForMood = (mood) =>
"https://www.ag-grid.com/example-assets/smileys/" +
(mood === "Happy" ? "happy.png" : "sad.png");
const [mood, setMood] = useState(imageForMood(props.value));
useImperativeHandle(ref, () => {
return {
refresh(params) {
setMood(imageForMood(params.value));
},
};
});
return <img width="20px" src={mood} alt="" />;
});
<AgGridReact
...
frameworkComponents={{
genderCellRenderer: GenderRenderer,
moodCellRenderer: MoodRenderer,
myCellRenderer: MyRenderer,
ageCellRenderer: AgeRenderer,
}}
>
AgGridReact 에 frameworkComponents 에 컴포넌트를 지정한다.
<AgGridColumn
headerName="Rendered Value"
field="value"
cellRendererSelector={(params) => {
const moodDetails = { component: "moodCellRenderer" };
const genderDetails = {
component: "genderCellRenderer",
params: {
values: ["Male", "Female"],
},
};
if (params.data.type === "gender") return genderDetails;
else if (params.data.type === "mood") return moodDetails;
else return undefined;
}}
/>
그리고 이렇게 지정해주면 된다.
이 cell이 gender 타입이면 genderCellRenderer로 지정했던 GenderRenderer 컴포넌트를 리턴하겠다는 뜻이고
mood이면 moodCellRenderer로 지정했던 MoodRenderer을 리턴하겠다는 뜻이다.
MoodRenderer컴포넌트는 위에서 봤듯이 mood 값이 happy냐 아니냐에 따라 다른 이모지가 나타난다.
셀마다 다른 컴포넌트를 넣을 때 사용할 수 있다.
'WEB > REACT' 카테고리의 다른 글
[REACT] redux-persist를 사용하여 새로고침 시에도 리덕스 상태 유지하기 (0) | 2022.02.08 |
---|---|
[REACT] slot machine 만들기 (0) | 2021.12.28 |
[REACT] react + typescript + recoil todo list 만들기 (0) | 2021.12.21 |
[REACT] react-table row selection/ column hidding (0) | 2021.12.17 |
[REACT] redux에 typescript적용하기 (0) | 2021.12.17 |