본문 바로가기
Study/Vue

VueJS 3 ) Chart.js + Ag-Grid-Vue3 연결(?)하기

by JongIk 2022. 3. 24.
반응형

chart.js + ag-grid-vue3

문제사항

위와 같이 같은 데이터를 나타내는 차트와 그리드가 있습니다.
우측의 그리드에서 데이터가 변경되었을 때 차트 또한 업데이트되도록 구현하고 싶었습니다.

사용한 json 데이터는 다음과 같습니다.

[
    {
        "name": "mark",
        "score": 100
    },
    {
        "name": "Nick",
        "score": 80
    },
    {
        "name": "Jack",
        "score": 60
    },
    {
        "name": "Mary",
        "score": 55
    },
    {
        "name": "John",
        "score": 90
    }
]

해결 과정

순서

  1. Props 로 동일한 data 공유하기
  2. Grid 에서 변경된 데이터를 emit 을 이용해 App.vue 의 기존 데이터에 덮어쓰기
  3. :key 속성 이용하기 (Re-Rendering)

1. Props 를 사용해 동일한 Data 를 사용하도록 했습니다.

  • App.vue
<template>
  <div>
    <div><h1>Chart And Grid</h1></div>
    <div class="container">
      <Chart :scoreData="state.scoreData"></Chart>
      <Grid :scoreData="state.scoreData"></Grid>
    </div>
  </div>
</template>

<script setup>
import Chart from "./views/Chart.vue";
import Grid from "./views/Grid.vue";
import Score from "./database/score.json";
import { reactive } from "vue";

const state = reactive({
  scoreData: Score,
});
</script>
  • Grid.vue
<!-- template code -->
    <ag-grid-vue
      style="width: 500px; height: 300px"
      class="ag-theme-alpine"
      :columnDefs="GridData.columnDefs"
      :rowData="GridData.rowData"
    >
    </ag-grid-vue>

<!-- script code -->
<script setup>
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-alpine.css";
import { AgGridVue } from "ag-grid-vue3";
import { defineProps, reactive } from "@vue/runtime-core";
const props = defineProps({
  scoreData: Array,
});

const GridData = reactive({
  columnDefs: [
    { headerName: "이름", field: "name" },
    { headerName: "점수", field: "score", editable: true },
  ],
  rowData: props.scoreData,
});

</script>
  • Chart.vue
<!-- template code -->
<canvas style="width: 500px; height: 300px" id="myChart"></canvas>

<!-- script code -->
<script setup>
import { addColor, getNameAndScore } from "../utils/index.js";
import { onMounted, defineProps } from "vue";
import Chart from "chart.js";

const props = defineProps({
  scoreData: Array,
});

// 이름과 점수만 들어있는 배열로 뽑아냅니다.  
const nameArr = getNameAndScore(props.scoreData).name;
const scoreArr = getNameAndScore(props.scoreData).score;

console.log(scoreArr);

onMounted(() => {
  var ctx = document.getElementById("myChart").getContext("2d");
  var myChart = new Chart(ctx, {
    type: "bar",
    data: {
      labels: nameArr,
      datasets: [
        {
          data: scoreArr,
          backgroundColor: addColor(props.scoreData), // 대충 랜덤색 생성
        },
      ],
    },
    // options
  });
});
</script>

위처럼 차트와 그리드에 입력될 데이터는 App.vue 에 있는 scoreData 를 Props 로 받아오도록 했습니다.


2. Ag-Grid-Vue3 의 onCellEditingStopped 속성을 사용해 셀 편집이 끝난 경우 emit 이벤트를 실행하도록 했습니다.

<!-- template code -->
<ag-grid-vue
      style="width: 500px; height: 300px"
      class="ag-theme-alpine"
      :columnDefs="GridData.columnDefs"
      :rowData="GridData.rowData"
      :onCellEditingStopped="change"
    >
</ag-grid-vue>

<!-- script code -->
<script setup>
  // 생략 : 위 코드와 동일합니다.
  // import { defineProps, defineEmits, reactive } from "@vue/runtime-core";
const emit = defineEmits(["changeScore"]);

const change = () => {
  emit("changeScore", GridData.rowData);
};
</script>
  • App.vue
<Grid @changeScore="changeData" :scoreData="state.scoreData"></Grid>

<!-- script code -->
<script setup>
  // 생략 : 위 App.vue 코드와 동일합니다.
function changeData(data) {
  for (let i = 0; i < data.length; i++) {
    data[i].score = Number(data[i].score);
  }
  state.scoreData = data;
  console.log(data);
}
</script>

emit 을 통해 변경된 값을 App.vue 로 보낸 다음, 기존의 scoreData 변수에 변경된 data 값을 넣었습니다.


3. :key 속성을 이용해 Chart 컴포넌트를 Re-Rendering 해주면 됩니다.

:key 속성은 가리키는 값에 변화가 일어나면 해당 컴포넌트를 Re-Render 해줍니다.

<!-- template code -->
<template>
  <div>
    <div><h1>Chart And Grid</h1></div>
    <div class="container">
      <Chart :key="state.render" :scoreData="state.scoreData"></Chart>
      <Grid @changeScore="changeData" :scoreData="state.scoreData"></Grid>
    </div>
  </div>
</template>

<!-- script code -->
<script setup>
// 생략
const state = reactive({
  scoreData: Score,
  render: 0,
});

function changeData(data) {
  for (let i = 0; i < data.length; i++) {
    data[i].score = Number(data[i].score);
  }
  state.scoreData = data;
  state.render++;
}
</script>

Vue 3 에서는 updateForce() 기능이 사라졌습니다.
구현을 위해 임시로 사용했기 때문에 이 방법이 가장 효율적인 방법은 아닐 수 있습니다.
더 효율적인 방법을 아시는 분은 좀 알려주세요..


결과


사용 라이브러리

ag-grid-vue3, chart.js@2.9.4 를 사용했습니다.

반응형

댓글