2025.03.12 - [React] - React(리액트) 티키타카 25 (AG-Grid 활용 1)
글에 다 설명하지 못한 걸 이어 달리기로 속도 줄임없이 가보장.
먼저 이름 컬럼에 검색 기능을 괘니 넣어보장. 왱? 쉬워서 안 쓸 수 없당.
data.js 의 field:name 정의 부분 끝자락에 filter:true 부터 넣어보장.
const colDefs = [
{
field: "name",
headerName: "이름",
flex: 2,
valueFormatter: (p) => {
// console.log("체킁2:", p); // 누느로 확인하게 뽀인또
return `${ranEmoji()} ${p.data.name}`;
},
filter: true, // 이것만 먼저 넣어보고 결과 확인
floatingFilter: true, // 요것도 넣어보고 결과 확인
},
아래 화면은 floatingFilter:true까지 넣은 결과당. 구시당.
검색 텍스트 창 옆 이상하게 생긴 아이콘도 눌러본당. 더 구시당~~, 선택은 본인 몫!
한가지 짚으로 덮고 넘어가면 안되는 거이 있당. 아래 페이지 내용 출력을 보면
해당 페이지에서만 검색된 게 아이공, 전체 리스트에서 검색 되었음을 의식하장.
이제 괘니 다른 컬럼에도 적용해 보는 연습을 해보장.
이제 사용자 정의 Cell , 멋진 말로 Custom Cell 곧 Cell 모양을 내가 만든단 이야기당.
이걸 간단히 한번 해보장, 설명은 어려운데, 이상하게 보면 바로 들켜버린당.
passdate(합격일) 컬럼을 타켓으로 적용해 보장.
{
field: "passdate",
headerName: "합격일",
cellRenderer: MyPassdate, // 이거당
flex: 2,
},
dummy 폴더 안에 아래 파일을 만들장. style 무시하면 단순 input type=date당
일부러 파일명과 파일 안의 함수명을 일치시키지 않았당.(이럴수도 있구낭? 공부용!)
MyCustom.jsx
import React from "react";
function MyPassdate(props) {
return (
<input
type="date"
style={{
border: "none",
width: "80%",
height: "100%",
backgroundColor: "yellow",
color: "blueviolet",
fontSize: "1.2em",
fontWeight: "bolder",
textAlign: "center",
}}
defaultValue={props.value}
/>
);
}
export default MyPassdate;
이제 data.js 파일의 최상단에 아래 라인을 추가 시킨당.
data.js
import MyPassdate from "./MyCustom";
// 이미티콘 단축키는 window키와 함께 ;를 또는 window키와 함께 .을 눌러줘용
const emoticons = [
... 생략
새로 고침 한 뒤 합격일 컬럼이 클릭하면 아래 처럼 동작함을 확인할 수 있당.(좋겠당!)
느끼미 덜 왔다면 괘니 1개 더 해보도록 하장. 기사자격증 소유 여부~
아래 파일을 dummy 폴더 아래에 만들장. (내 맘인데, 당신 맘대로 해도 머라 안한당!)
MyGisa.jsx
import React from "react";
function MyGisa({ value }) {
return <input type="checkbox" defaultChecked={value} />;
}
export default MyGisa;
data.js 파일에 아래 처럼 쪼메만 추가하도록 하장.
import MyPassdate from "./MyCustom";
import MyGisa from "./MyGisa"; // 추가
... 생략
{
field: "gisa",
headerName: "기사",
cellRenderer: MyGisa, // 추가
flex: 1
},
.... 생략
새로 고침하고 결과를 확인하면, 기사 컬럼에 체크/해제 가능한 체크박스가 보인당.
이 결과는 구지 캡처하지 않겠당... [ 왱? 힘드니깡!!~~ ㅋㅋ]
이렇게 커스터마이징을 직접 할 수도 있지만, 사용자들이 많이 반복적으로 하는
커스터마이징 하는 패턴을 찾아낸 Ag-Grid에서 아래와 같은
7개의 미리 만들어진 Cell-Editor를 제공한당. (역시 훌륭하당!)
1. agTextCellEditor => text
2. agLargeTextCellEditor => textarea
3. agSelectCellEditor => select
4. agRichSelectCellEditor => dropdown
5. agNumberCellEditor => number
6. agDateCellEditor => date
7. agCheckboxCellEditor => checkbox
이 중에 agSelectCellEditor1개만 같이 해보공, 나머진 알아서 해보장.
문서를 읽어봐야 할 수도 있당. (외면 하지 말장. 외면하면 외면 당한당)
data.js 의 특징(feature) 컬럼 정의 부분을 아래 처럼 수정하장
{
field: "feature",
headerName: "특징",
flex: 1,
editable: true, // 요게 있어야 편집 가능하당.
sortable: false, // 요건 재미로 넣었당. sort 동작 안하겡
cellEditor: "agSelectCellEditor", // 추가
cellEditorParams: { // 선택사항 내맘대로 추강
values: [
"탑모델",
"귀요미",
"매니저",
"아이돌",
"연예인",
"브레인",
"왕부자",
"개그왕",
"또머거",
"웃긴당",
],
},
},
이제 실행 결과를 보면 아래와 같을 지어당. (역시 좋당. 느끼미가 마구 온다낭!)
선택에 관련된 것도 1개 해보장.
GridExam.jsx 파일에 아래 처럼 rowSel... 를 추가해 보장.
... 생략
<AgGridReact
theme={myTheme}
// 아래 코드 추가
rowSelection={{
mode: "multiRow",
// checkboxes: false,
// headerCheckbox: false,
}}
pagination={true}
.... 생략
결과를 눈으로 확인 후, 주석도 1개도 열어서 어떻게 바뀌는지 보장. (그냥 알게 될꺼이당!)
화면 캡처는 기꺼이 생략하고, 마지막에 한번 더 붙여보겠당.~~
이제 이벤트 1~2개는 정 없으니 3개만 넣어서 동작을 보도록 하장
제공 되는 이벤트 리스트는 아래 링크에 가면 나오는데... 헉 부담스럽도록 많당.
필요에 의해 찾아보는 시간을 가질수만 있다면, 당신은 이미 너무 훌륭하당.
https://www.ag-grid.com/react-data-grid/grid-events/
자주 쓰일 꺼 같은 느낌(개인적 착각!!)의 이벤트 3개만 넣어 보도록 하장.
GridExam.jsx 파일에 아래처럼 추가하장.
function GridExam() {
... 생략
// 아래 3개 이벤트 핸들러 추가
const cellClick = (e) => {
console.log("cellClick:", e);
};
const rowClick = (e) => {
console.log("rowClick:", e);
};
const rowSelect = (e) => {
console.log("rowSelect: ", e);
};
return (
<div style={{ height: 500 }}>
<AgGridReact
theme={myTheme}
... 생략
// 아래 3개 추가
onCellClicked={cellClick}
onRowClicked={rowClick}
onRowSelected={rowSelect}
/>
</div>
);
}
이제 브라우저에서 새로 고침하고, F12를 눌러서 console로 선택해 놓고
아래 그림 처럼 클릭, 선택을 하면 느끼미가 풍부한 이벤트 데이터가 누네 번쩍인당.
거기서 필요한 값을 골라쓰면 된당. (아마도 관찰 시간이 쪼메 필요할꺼당.)
원래 의도는 비동기로 데이터를 초기화 하고, 입력창까지 하려 했지만, 오늘은 힘들당.
CSS 적용 테크닉까지 담은 전체 소스를 나열하고, 다음을 기약해 보장.
MyCustom.jsx 와 MyGisa.jsx는 변경내용 없공.
index.css 에 약간의 css 추가 (page 부분 stying 포함)
.name-green {
background-color: blueviolet;
color: white;
text-align: center;
}
.brain-inc {
background-color: aquamarine;
}
.ag-paging-panel {
background-color: black;
color: yellow;
}
#ag-6-display {
color: blue;
font-weight: bolder;
}
#ag-4 div.ag-picker-field-icon > span {
color: red;
}
.ag-header-cell-text {
margin: 0 auto;
text-align: center;
color: blueviolet;
font-size: 1.2em;
}
data.js 전체소스
import MyPassdate from "./MyCustom";
import MyGisa from "./MyGisa";
// 이미티콘 단축키는 window키와 함께 ;를 또는 window키와 함께 .을 눌러줘용
const emoticons = [
"👩",
"👧",
"👵",
"👩🦰",
"👱",
"👸",
"🤶",
"👮♀️",
"🕵️♀️",
"👩⚕️",
"👩🎓",
"👰",
"👩🏫",
"👩⚖️",
"👩🍳",
"👩🏭",
"👩💼",
"👩🔬",
"👩💻",
"👩🎨",
"👩✈️",
"👩🚀",
"🧕",
];
const ranEmoji = () => emoticons[Math.floor(Math.random() * emoticons.length)];
const ranAge = () => Math.round(Math.random() * 10) + 20;
// 초기 가짜 데이터
const rowData = [
{
name: "추우니",
feature: "탑모델",
passdate: "2024-10-10",
age: ranAge(),
gisa: true,
},
{
name: "영시니",
feature: "귀요미",
passdate: "2024-11-11",
age: ranAge(),
gisa: true,
},
{
name: "세이니",
feature: "매니저",
passdate: "2024-12-12",
age: ranAge(),
gisa: true,
},
{
name: "지혀니",
feature: "브레인",
passdate: "2025-01-01",
age: ranAge(),
gisa: true,
},
{
name: "사나니",
feature: "옌예인",
passdate: "2025-02-02",
age: ranAge(),
gisa: true,
},
{
name: "E7E니",
feature: "아재개긍",
passdate: "",
age: ranAge() + 20,
gisa: false,
},
];
// Just For Test
Array.from({ length: 108 }).forEach((_, i) => {
rowData.push({
name: "머라니" + i,
feature: "특징" + i,
age: ranAge() + 20,
gisa: i % (i + i) ? true : false,
});
});
const colDefs = [
{
field: "name",
headerName: "이름",
valueGetter: (p) => {
// console.log("체킁1:", p); // 이해를 위한 뽀인또
return p.data.name;
},
valueFormatter: (p) => {
// console.log("체킁2:", p); // valueGetter가 실행된 이후에 실행 됨이 뽀인또
return `${ranEmoji()} ${p.data.name}`;
},
flex: 1,
filter: true,
floatingFilter: true,
cellClassRules: {
"name-green": () => true,
},
},
{
field: "feature",
headerName: "특징",
flex: 1,
editable: true,
sortable: false,
cellEditor: "agSelectCellEditor",
cellEditorParams: {
values: [
"탑모델",
"귀요미",
"매니저",
"아이돌",
"연예인",
"브레인",
"왕부자",
"개그왕",
"또머거",
"웃긴당",
],
},
},
{
field: "passdate",
headerName: "합격일",
cellRenderer: MyPassdate,
flex: 2,
},
{
field: "age",
headerName: "나이",
flex: 1,
filter: true,
floatingFilter: true,
},
{ field: "gisa", headerName: "기사", cellRenderer: MyGisa, flex: 1 },
];
const dummy = { rowData, colDefs };
export default dummy;
GridExam.jsx 전체소스(현재 설명까정...)
/* eslint-disable no-unused-vars */
import { AllCommunityModule, ModuleRegistry } from "ag-grid-community";
import { AgGridReact } from "ag-grid-react";
import dummy from "./dummy/data-back";
import { useState } from "react";
import { iconSetAlpine, themeQuartz } from "ag-grid-community";
ModuleRegistry.registerModules([AllCommunityModule]);
const myTheme = themeQuartz.withPart(iconSetAlpine).withParams({
accentColor: "#0086F4",
backgroundColor: "#F1EDE1",
borderColor: "#98968F",
borderRadius: 0,
browserColorScheme: "light",
chromeBackgroundColor: {
ref: "backgroundColor",
},
fontFamily: {
googleFont: "Pixelify Sans",
},
fontSize: 15,
foregroundColor: "#605E57",
headerBackgroundColor: "#E4DAD1",
headerFontSize: 15,
headerFontWeight: 700,
headerTextColor: "#3C3A35",
rowVerticalPaddingScale: 1.2,
spacing: 5,
wrapperBorderRadius: 0,
});
function GridExam() {
// 초기 데이터
const [rowData, setRowData] = useState(dummy.rowData);
const [colDefs, setColDefs] = useState(dummy.colDefs);
// 이벤트
const cellClick = (e) => {
console.log("cellClick:", e);
};
const rowClick = (e) => {
console.log("rowClick:", e);
};
const rowSelect = (e) => {
console.log("rowSelect: ", e);
};
// row 선택 모드
const rowSel = () => {
return {
mode: "multiRow", // 디폴트는 singleRow
// checkboxes: false,
// headerCheckbox: false,
};
};
const rowClassRules = {
"brain-inc": (params) => {
//console.log("체으킁:", params);
return !(params.rowIndex % 2);
},
};
return (
<div style={{ height: 500 }}>
<AgGridReact
theme={myTheme}
// 함수로 수정
rowSelection={rowSel()}
rowClassRules={rowClassRules}
pagination={true}
paginationPageSize={10}
paginationPageSizeSelector={[5, 10, 15, 20]}
rowData={rowData}
columnDefs={colDefs}
// 아래 3개 추가
onCellClicked={cellClick}
onRowClicked={rowClick}
onRowSelected={rowSelect}
/>
</div>
);
}
export default GridExam;
위 소스들의 연합으로 이루어진 결과는 아래와 같당.
그저 항상 도움이 되었으면 하는 마음이당.
혼자하면 금방 되는 거이, 정리는 어이 이리 힘들단 말인강!~~
( AI에게 시키란 말이당.!~~ )
금수저에게 보냈던 내 수호령이 돌아왔다.
지쳐서 돌아온 내 수호령 역시 잠시의 휴식만으로도
바로 나의 어질 어질 흔들림을 바로 잡는다.
서로의 웃음을 웃음으로 회복한다.
걱정이다. 근거없는 느끼미 그렇다.
금수저의 하늘에 먹구름이 몰려가는 기분이다
애시당초 선택이 선택을 왜곡한 것인가?
그저 금수저에게 아무일 없이 잘 넘어가길...
바람아 해빛아 좀 도와주지 않으렴
먹구름 봄비가 되어, 금수저를 따스하게 적시길.....
먹구름 아래 먹과 벼루를 놓아본다.
그리고 붓을 들어 기다린다.
종이는 철저히 먹구름에게서 숨겨야만 한다.
Lily 너도 날 이해 못하닝??
https://www.youtube.com/watch?v=sFH6MHWbGSU
React(리액트) 티키타카 29 (CkEditor) (0) | 2025.03.22 |
---|---|
React(리액트) 티키타카 28 (tanstack useInfiniteQuery) (0) | 2025.03.19 |
React(리액트) 티키타카 26 (bootstrap 아니면 tailwindcss) (0) | 2025.03.14 |
React(리액트) 티키타카 25 (AG-Grid 활용 1) (0) | 2025.03.14 |
React(리액트) 티키타카 24 (useDebounce Hook 2) (2) | 2025.03.12 |