frontend-collective / react-sortable-tree

Drag-and-drop sortable component for nested data and hierarchies
https://frontend-collective.github.io/react-sortable-tree/
MIT License
4.91k stars 902 forks source link

How to get `parentKey` to add new node #49

Closed anil1712 closed 7 years ago

anil1712 commented 7 years ago

Hi, First of all I really like your implemented stuff, it saves my lot of time. So thanks for implementing this sortable tree, but it would be more better if you can make the documentation more descriptive. Because I am having issue while using the Data Helper Functions. I am trying to use the addNodeUnderParent(treeData, newNode, parentKey) but dont know how to get the parentKey.

Also I did not the use of getNodeKey parameter, so can you please explain these thing with some example.

Actually I am pretty new for React. So please help me to get out of it.

@fritz-c can you please look into it.

Thanks

imyagnesh commented 7 years ago

Hi,

Can you provide example for AddNodeUnderParent and removeNodeAtPath i am trying to add and remove
node in generateNodeProps property.

@anil1712 : Please let me know if you found any work arround.

Thanks in advance.

anil1712 commented 7 years ago

@yagneshmodh Right now I am just using addNodeUnderParent. Here is the working code.

..................
import {addNodeUnderParent} from 'react-sortable-tree';
..................
let NEW_NODE = {title: 'Another Node', subtitle: 2}
let newTree = addNodeUnderParent({
        treeData: this.state.treeData,
        newNode: NEW_NODE,
        expandParent: true,
        parentKey: 0, // Still don't know where to get the parentKey
        getNodeKey: ({node: TreeNode, treeIndex: number}) => {
            return treeIndex;
       },
 });
 this.setState({treeData: newTree.treeData});

Let me know if you got any solution to pass the parentKey

anil1712 commented 7 years ago

@fritz-c Can you please look into this?

vladimirsvsv77 commented 7 years ago

try this code:

    addNode(rowInfo){
        let NEW_NODE = {title: 'Another Node', subtitle: 2};
        let {node, treeIndex, path} = rowInfo;
        path.pop();
        let parentNode = getNodeAtPath({
            treeData: this.state.treeData,
            path : path,
            getNodeKey: ({ treeIndex }) =>  treeIndex,
            ignoreCollapsed : true
        });
        let getNodeKey = ({ node: object, treeIndex: number }) => {
            return number;
        };
        let parentKey = getNodeKey(parentNode);
        if(parentKey == -1) {
            parentKey = null;
        }
        let newTree = addNodeUnderParent({
                treeData: this.state.treeData,
                newNode: NEW_NODE,
                expandParent: true,
                parentKey: parentKey,
                getNodeKey: ({ treeIndex }) =>  treeIndex
         });
         this.setState({treeData: newTree.treeData});
    }

It's works fine for me.

fritz-c commented 7 years ago

Sorry for the delay.

What action initiates the node addition? i.e., are you clicking on a button on the parent to add a child, are you adding nodes programmatically to parent nodes that fit certain conditions, etc? The way to do it will depend on that.

anil1712 commented 7 years ago

Very very thanks @fritz-c Its working prefect.

beratuslu commented 7 years ago

guys nobody has explained where to get parentKey to add new node? in my case I am triggering an action from inside custom node renderer.

everyonesdesign commented 6 years ago

I wrote a small helper function to get the parent. I know it's not that efficient, because it walks every node of the tree even after it has found the parent, but anyway:

function getNodeKey(node) {
  return node.id;
}

function getParent(childNode, treeData) {
  let parent = null;

  walk({
    treeData,
    getNodeKey,
    ignoreCollapsed: false,
    callback: ({ node }) => {
      if (find(node.children, { id: getNodeKey(childNode) })) {
        parent = node;
      }
    },
  });

  return parent;
}

You may want to alter getNodeKey function depending on your tree structure.

borisyordanov commented 6 years ago

@vladimirsvsv77 What is getNodeAtPath()? Is that a system function? If you wrote it yourself can you post your code?

borisyordanov commented 6 years ago

@everyonesdesign I don't understand your function. How do you just call walk(), isn't that a method of the treeData? Is it a custom method you implemented?

What about find()? Where did that come from?

everyonesdesign commented 6 years ago

@borisyordanov walk is a utils method provided by react-sortable-tree. It can be directly imported from it, e.g.

const { walk } = require('react-sortable-tree');

You can find other helpful functions in src/utils/tree-data-utils.js. As for find method, I use the method from Lodash library, but you can also use native Array.prototype.find (in case you don't support IE or polyfill the method).

if (node.children.find(child => getNodeKey(сhild) === getNodeKey(childNode))) {
  parent = node;
}
borisyordanov commented 6 years ago

@everyonesdesign Thanks for taking the time to explain. I do have another question - if you need to get the parent of the node, why don't you use the path? If your path = [2,3,1], then the parent should be treeData[2].children[3] ?

Edit: i guess what i posted above is true, but that info doesn't necessarily make finding the parent easier. I ended up using a slightly modified version of your function. Thanks for the help!

stephencarr commented 6 years ago

Late to the game, but @borisyordanov import { getNodeAtPath } from 'react-sortable-tree'

metalalive commented 3 years ago

guys nobody has explained where to get parentKey to add new node? in my case I am triggering an action from inside custom node renderer.

The parentKey comes from the property generateNodeProps in your SortableTree component , generateNodeProps points to any function with arguments {node, path} and expects to return an a object with fields like {title: YOUR_NODE_TITLE, buttons:[BUTTON1, BUTTON2 , ......]} which is used for renderring each node . For example , your render code may look like this :

 render() {
        return <SortableTree isVirtualized={ false }
                treeData={this.state.treeData}
                onChange={this.on_state_change.bind(this)}
                generateNodeProps={
                    ({node, path, treeidx}) => ({
                        title: node.title,
                        buttons: [
                            <button data-node_key_path={ path.join(',') }
                                onClick={ this.update_tree_status.bind(this) }>
                                edit
                            </button> ,
                        ],
                    })
                }
            /> ;
    } // end of render function

The argument path in the property generateNodeProps is actually a list of tree index numbers for each node to represent a path from a root node to that node in current tree view , Note that the path of each node also changes as soon as you expand or collapse any non-leaf node in the tree view, then Sortable re-calculates path and re-render the buttons for each present node in updated tree view .

To me, it may be good practice to preserve path from generateNodeProps to the buttons (in the example, the path is serialized and store to dataset of the HTML element) , used at later time for any operation on the tree.

Hope that helps, since it is not clearly documented.