-1

I am trying the example at the below link (Dragging Multiple Records Between Grids) within my React application https://www.ag-grid.com/react-data-grid/row-dragging-to-grid/

I have a few questions;

  1. For the right/selected grid, how can I get the selected rows? Can I get that from "rightApi" ? Is using something like below fine ?

    rightApi.rowModel.forEachNode((tmp) => {console.log(tmp)})

  2. Is it possible to limit the number of selected/draggable rows on the right i.e. say I want to only allow 10 records in the right/selected grid ?

  3. The moment I am adding the last column for 'Delete' icon for the right grid (cellRenderer: SportRenderer), I am getting an error when I try to drag any row to the right (Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.) Below is the code for the same;

    const SportRenderer = (props) => {
      return (
        <i
          className="far fa-trash-alt"
          style={{ cursor: 'pointer' }}
          onClick={() => props.api.applyTransaction({ remove: [props.node.data] })}
        ></i>
      );
    };

UPDATED

For 3rd point, I get the error "just" for the first time I drag/drop any row and not on subsequent row drag. Also, this is only if I enable cellRenderer: SportRenderer, in my rightColumns

Below is the stack trace;

Uncaught TypeError: Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'.
    at CellComp.afterCellRendererCreated (ag-grid-community.cjs.js:21351:1)
    at eval (ag-grid-community.cjs.js:10291:1)
    at new AgPromise (ag-grid-community.cjs.js:10266:1)
    at AgPromise.then (ag-grid-community.cjs.js:10289:1)
    at createCellRendererFunc (ag-grid-community.cjs.js:21328:1)
    at AnimationFrameService.executeFrame (ag-grid-community.cjs.js:37211:1)

Below is what gets logged for 'props' inside SportRenderer

enter image description here

copenndthagen
  • 49,230
  • 102
  • 290
  • 442

1 Answers1

2

Question 1

You need to enable row selection for all grids. Currently, it is only available for the grid on the left side. Comment this line of code.

rowSelection={id === 0 ? 'multiple' : undefined}

Same as multiple row selection, change this line

rowSelection={id === 0 ? 'multiple' : undefined}

with this

rowSelection={'multiple'}

Now you can get the selected rows on the right grid like this

rightApi.getSelectedRows()

Question 2

Yes, it is possible. We'll use rightRowData for this purpose. In the example you mentioned, it's not actively used. Let's change our onDragStop method like below

const onDragStop = useCallback(
  (params) => {
    var data = [...rightRowData];

    var nodes = params.nodes;

    // allow only if we have 3 rows or less in the right grid
    if (data.length < 4) {
      if (radioChecked === 0) {
        var allowed = 4 - data.length;

        leftApi.applyTransaction({
          remove: nodes.slice(0, allowed).map(function (node) {
            return node.data;
          }),
        });
      } else if (radioChecked === 1) {
        leftApi.setNodesSelected({ nodes, newValue: false });
      }

      nodes.map(function (node) {
        if (data.length < 4) {
          data.push(node.data);
        }
      });
    }

    setRightRowData(data);
  },
  [leftApi, rightRowData, radioChecked]
);

The code above prevents users drag more than 4 rows. Let's say we have 1 row in the right grid. If a user drags more than 3 rows to the right, only 3 rows are going to be added to the right grid.

We should also set right row data when a user deletes a row from the right grid. Change SportRenderer like the following

const SportRenderer = (props) => {
  return (
    <i
      className="far fa-trash-alt"
      style={{ cursor: 'pointer' }}
      onClick={() => {
        // remove right grid data
        var rrd = [...props.rightRowData];
        rrd = rrd.filter((d) => d.athlete !== props.node.data.athlete);
        props.setRightRowData(rrd);
        props.api.applyTransaction({ remove: [props.node.data] });
      }}
    ></i>
  );
};

But hey, we need to get our hands on rightRowData and setRightRowData. For that purpose, we move our rightColumns inside GridExample component wrapping it by useMemo and pass rightRowData and setRightRowData as cellRendererParams

const rightColumns = useMemo(
  () => [
    {
      rowDrag: true,
      maxWidth: 50,
      suppressMenu: true,
      rowDragText: (params, dragItemCount) => {
        if (dragItemCount > 1) {
          return dragItemCount + ' athletes';
        }
        return params.rowNode.data.athlete;
      },
    },
    { field: 'athlete' },
    { field: 'sport' },
    {
      suppressMenu: true,
      maxWidth: 50,
      cellRenderer: SportRenderer,
      // pass rightRowData and setRightRowData as cellRendererParams
      cellRendererParams: {
        rightRowData: rightRowData,
        setRightRowData: setRightRowData,
      },
    },
  ],
  [rightRowData, setRightRowData]
);

Question 3

Well, I have no idea. It works in my case.

Working plunker: https://plnkr.co/edit/8yKp3eRFm9SiOBnE?preview

UPDATE

Okay, you wanna populate both grids according to your server response. Let's say Michael Phelps is a selected athlete and he exists on the right grid. To do that

const loadGrids = useCallback(() => {
  setLeftRowData([...rawData]);
  // we manually set here, you can set the selected athletes with your server response just like I did here
  setRightRowData([
    {
      "athlete": "Michael Phelps",
      "age": 23,
      "country": "United States",
      "year": 2008,
      "date": "24/08/2008",
      "sport": "Swimming",
      "gold": 8,
      "silver": 0,
      "bronze": 0,
      "total": 8
    }
  ]);
  leftApi.deselectAll();
}, [leftApi, rawData]);

and after my fetch, I manually skip Michael Phelps to prevent keeping it on the left grid because he's a selected athlete

useEffect(() => {
  if (!rawData.length) {
    fetch('https://www.ag-grid.com/example-assets/olympic-winners.json')
      .then((resp) => resp.json())
      .then((data) => {
        const athletes = [];
        let i = 0;

        while (athletes.length < 20 && i < data.length) {
          var pos = i++;
          // skip Michael Phelps
          if (athletes.some((rec) => rec.athlete === data[pos].athlete) || data[pos].athlete == "Michael Phelps") {
            continue;
          }
        athletes.push(data[pos]);
      }
      setRawData(athletes);
    });
  }
}, [rawData]);
Ahmet Firat Keler
  • 2,603
  • 2
  • 11
  • 22
  • Thanks a lot for the detailed response. Just one last question. For my use case, I'll have both Add and Modify. Now for Modify, I need to prepopulate the right grid. So how can I prepopulate the right grid from my server response which is just a simple array ? – copenndthagen Aug 01 '23 at 19:20
  • Do you mean redrawing grid from the beginning using your server response? – Ahmet Firat Keler Aug 01 '23 at 19:25
  • Yeah right..So assume after the initial Add (of say list of selected students) and user submits to server....Now, user comes back to the grid and wants to see the current list of selected students that was earlier saved to the database. So in this case, kind of drawing the grid with the right grid prepopulated with the selected students.... – copenndthagen Aug 01 '23 at 19:28
  • Though just to add....I am also wondering how to sync....since the left grid is populated based on a server lookup I have above the grid. User can always do the lookup which populates the left grid and he then selects/drag the selected ones to the right....For uniqueness, I have studentId against each row...So somehow would have to leverage that – copenndthagen Aug 01 '23 at 19:30
  • I have updated my answer. Does it help? – Ahmet Firat Keler Aug 01 '23 at 19:44
  • Awesome Man...This is way too excellent....Everything is now good....just that I am still getting the error "just" for the first time I drag/drop any row (Have added UPDATED section in my original question with details around the same) "Failed to execute 'appendChild' on 'Node': parameter 1 is not of type 'Node'." – copenndthagen Aug 02 '23 at 06:51
  • It would be easier to help for me if you share it on an online code editor – Ahmet Firat Keler Aug 02 '23 at 07:05
  • Oh k...yeah, I understand...Just that it would take me some time to do that, since the AgGrid is actually an internal library that I am using....Meanwhile was just wondering if I can try some other alternative for deletion from right...like may be left/right arrows between the grid to move items around... – copenndthagen Aug 02 '23 at 07:29
  • Please log 'props' on console inside SportRenderer – Ahmet Firat Keler Aug 02 '23 at 08:35
  • Apologies for the delay in responding – copenndthagen Aug 02 '23 at 17:18
  • I get an object for 'props' logged after I drag any row for the 1st time...I have added a screenshot in the original question under UPDATED – copenndthagen Aug 02 '23 at 17:25
  • Just to add...passing SportRenderer as string fixes the issue i.e. cellRenderer: 'SportRenderer' (though just testing further to see if it is not breaking anything) – copenndthagen Aug 02 '23 at 17:30
  • What is your Ag-Grid version? – Ahmet Firat Keler Aug 03 '23 at 15:00
  • One more thing which I just realized is that for the max allowable records on the right, even though I have the exact same code that you added for "onDragStop" method, it displays additional rows on the right even after adding the max (4)...even though when I debug on the last line under onDragStop definition i.e. for setRightRowData(data), data is just set to 4 elements, but it displays every row dragged to the right and does not respect the limit i.e. 4 – copenndthagen Aug 05 '23 at 06:07
  • ...though, the moment I hit Delete on the right for any row (say when it is displaying 7 rows), it kind of auto-corrects and shows (4-1) = 3 rows...not sure if something to do with applyTransaction ? – copenndthagen Aug 05 '23 at 06:11
  • Pls let me know if any additional info is needed from my end for the max allowable records issue – copenndthagen Aug 05 '23 at 13:44
  • I'll take a look, hold on! – Ahmet Firat Keler Aug 06 '23 at 06:57
  • Do not miss this prop in your AgGridReact component -> rowData={id === 0 ? leftRowData : rightRowData} It should be something related with setRightRowData in the last line of onDragStop method. Please put a console log one line before and watch data. Array count should not exceed 4 even if you drag and drop 5th row. – Ahmet Firat Keler Aug 06 '23 at 07:39
  • So, I do have rowData={id === 0 ? leftRowData : rightRowData}...Also when I console log data just before setRightRowData(data) in onDragStop, I see that, dragging after adding 4 rows, it only shows array of first 4 rows added and does not show the 5th, 6th, etc which are dragged...However, they are still getting added in the right grid.. – copenndthagen Aug 06 '23 at 12:37
  • ....though, the moment I hit Delete on the right for any row (say when it is displaying 7 rows), it kind of auto-corrects and shows (4-1) = 3 rows – copenndthagen Aug 06 '23 at 12:38
  • Please dig through this condition: if (data.length < 4) . If you comment 158th and 171st lines in the plunkr I shared with you, you'll see all rows are going to be added to the grid on the right. This is your starting point to solve this puzzle. – Ahmet Firat Keler Aug 07 '23 at 09:29