bumbeishvili / org-chart

Highly customizable org chart. Integrations available for Angular, React, Vue
https://stackblitz.com/edit/web-platform-o5t1ha
MIT License
927 stars 330 forks source link

Is it possible to prevent the second time I draw a d3-org-chart from referencing the previous chart situation? #271

Closed makepado closed 1 year ago

makepado commented 1 year ago

The second time I plotted the d3-org-chart, I used the it references the previous dataset, whether it collapsed or not, how can we prevent this?

  1. Based on the above website
  2. Expand any nodes.
  3. Write the following code in the console
    d3.csv(
        'https://raw.githubusercontent.com/bumbeishvili/sample-data/main/org.csv'
      ).then((dataFlattened) => {
        chart = new d3.OrgChart()
          .container('.chart-container')
          .data(dataFlattened) // https://gist.github.com/bumbeishvili/40fc87bdcdc4d7a1919e4cce2fed10f9
          .render();
      });

    It will be replaced with a chart of the previous situation.

Not because it's the same data, If you've added other data you'll see a chart with the old data when you press the expand button.

https://user-images.githubusercontent.com/76645966/234821656-a0dcea99-c0ca-4e78-8236-f9c8455fbc8a.mp4

bumbeishvili commented 1 year ago

Hi, did not exactly understand the issue, can you rephrase it differently?

makepado commented 1 year ago

In short, when I redraw a chart, I did 'chart.data(datas...).render()' to redraw the chart. However, the old data is being exposed.

Therefore, I would like to ask if there is a feature that allows me to reset the chart.

I uploaded a temporary CSV to my GitHub repo. My CSV and your CSV have different data.

https://raw.githubusercontent.com/dr-hongjae/test-d3-org-chart-csv/main/temp_hongjae.csv

I tested it with the site here. https://stackblitz.com/edit/web-platform-jyncb9?file=index.html

  1. on this site is fetching and rendering data like this.

    d3.csv(
        'https://raw.githubusercontent.com/bumbeishvili/sample-data/main/org.csv'
      ).then((dataFlattened) => {
        chart = new d3.OrgChart()
          .container('.chart-container')
          .data(dataFlattened)
          .nodeWidth((d) => 250)
          .initialZoom(0.7)
          .nodeHeight((d) => 175)
          .childrenMargin((d) => 40)
          .compactMarginBetween((d) => 15)
          .compactMarginPair((d) => 80)
          .nodeContent(function (d, i, arr, state) {
            return `
            <div style="padding-top:30px;background-color:none;margin-left:1px;height:${
              d.height
            }px;border-radius:2px;overflow:visible">
              <div style="height:${
                d.height - 32
              }px;padding-top:0px;background-color:white;border:1px solid lightgray;">
    
                <img src=" ${
                  d.data.imageUrl
                }" style="margin-top:-30px;margin-left:${d.width / 2 - 30}px;border-radius:100px;width:60px;height:60px;" />
    
               <div style="margin-right:10px;margin-top:15px;float:right">${
                 d.data.id
               }</div>
    
               <div style="margin-top:-30px;background-color:#3AB6E3;height:10px;width:${
                 d.width - 2
               }px;border-radius:1px"></div>
    
               <div style="padding:20px; padding-top:35px;text-align:center">
                   <div style="color:#111672;font-size:16px;font-weight:bold"> ${
                     d.data.name
                   } </div>
                   <div style="color:#404040;font-size:16px;margin-top:4px"> ${
                     d.data.positionName
                   } </div>
               </div> 
               <div style="display:flex;justify-content:space-between;padding-left:15px;padding-right:15px;">
                 <div > Manages:  ${d.data._directSubordinates} 👤</div>  
                 <div > Oversees: ${d.data._totalSubordinates} 👤</div>    
               </div>
              </div>     
      </div>
    `;
          })
          .render();
  2. I opened the bottom right console, and typed in the code to load my CSV.

d3.csv(
        'https://raw.githubusercontent.com/dr-hongjae/test-d3-org-chart-csv/main/temp_hongjae.csv'
      ).then((dataFlattened) => {
        chart = new d3.OrgChart()
          .container('.chart-container')
          .data(dataFlattened)
          .nodeWidth((d) => 250)
          .initialZoom(0.7)
          .nodeHeight((d) => 175)
          .childrenMargin((d) => 40)
          .compactMarginBetween((d) => 15)
          .compactMarginPair((d) => 80)
          .nodeContent(function (d, i, arr, state) {
            return `
            <div style="padding-top:30px;background-color:none;margin-left:1px;height:${
              d.height
            }px;border-radius:2px;overflow:visible">
              <div style="height:${
                d.height - 32
              }px;padding-top:0px;background-color:white;border:1px solid lightgray;">

                <img src=" ${
                  d.data.imageUrl
                }" style="margin-top:-30px;margin-left:${d.width / 2 - 30}px;border-radius:100px;width:60px;height:60px;" />

               <div style="margin-right:10px;margin-top:15px;float:right">${
                 d.data.id
               }</div>

               <div style="margin-top:-30px;background-color:#3AB6E3;height:10px;width:${
                 d.width - 2
               }px;border-radius:1px"></div>

               <div style="padding:20px; padding-top:35px;text-align:center">
                   <div style="color:#111672;font-size:16px;font-weight:bold"> ${
                     d.data.name
                   } </div>
                   <div style="color:#404040;font-size:16px;margin-top:4px"> ${
                     d.data.positionName
                   } </div>
               </div> 
               <div style="display:flex;justify-content:space-between;padding-left:15px;padding-right:15px;">
                 <div > Manages:  ${d.data._directSubordinates} 👤</div>  
                 <div > Oversees: ${d.data._totalSubordinates} 👤</div>    
               </div>
              </div>     
      </div>
  `;
          })
          .render();
      });
  1. Expand or collapse any node.
  2. The chart is referencing old data.

If you look at the name and positionName, these were originally written as test, but have been replaced with the old data.

https://user-images.githubusercontent.com/76645966/235028408-6a3a75b0-c7de-4ced-9647-266b7670bd9b.mp4

NMESteve commented 1 year ago

I was having a similar problem. Looks like it's an underlying issue with d3. Whenever I reload the data, I have to clear the d3 canvas first.

d3.select('svg').selectAll('*').remove();
bumbeishvili commented 1 year ago

You can reload the data just by using the same chart reference

d3.csv( 'https://raw.githubusercontent.com/dr-hongjae/test-d3-org-chart-csv/main/temp_hongjae.csv')
.then((dataFlattened) => {
   chart.data(dataFlattened).render()
})

When you are creating the new OrgChart() this does not clear the old org chart reference and it causes undesirable consequences

brunofurmon commented 15 hours ago

You can reload the data just by using the same chart reference

d3.csv( 'https://raw.githubusercontent.com/dr-hongjae/test-d3-org-chart-csv/main/temp_hongjae.csv')
.then((dataFlattened) => {
   chart.data(dataFlattened).render()
})

When you are creating the new OrgChart() this does not clear the old org chart reference and it causes undesirable consequences

2024 December on React - I solved the same issue by preventing reinitialization of OrChart instance as stated by @bumbeishvili (huge thanks!).

As I use react and keep the chart in a shareable state (useState), I had to first initialize it on an independent useEffect since DOM manipulation is done at ctor level, and config the shared chart instance on a different useEffect with chart dependency

Something like:

import React, { useRef, useEffect, useState } from "react";

const d3Container = useRef(null);
const [chart, setChart] = useState(null);

useEffect(() => {
  // initial chart set
  setChart(new OrgChart());
}, []);

useEffect(() => {
  // chart config
  if (!chart) return;

  // fetchUsers returns an array of user objects
  fetchUsers(...).then((users) => {
    chart
      .container(d3Container.current)
      .layout("top")
      .data(users)
      .render()
      .fit();
  }, [
    chart,
    // other dependencies
  ]);
});
// (...)

return (
  // (...)
  <div ref={d3Container} />
  // (...)
);