Andrew Steed
Andrew Steed

Reputation: 63

material-ui dynamic nestedItems in List from JSON data

I'm building a node tree in React.js using { List, ListItem, MakeSelectable} from 'material-ui/List'. I will eventually wire up JSON data coming from an internal web service call. I have no problem declaratively creating a nested list:

<List>
...
<ListItem
    ...
    nestedItems={[
      <ListItem
        ...

What I want to do is programmatically create this nested structure from the JSON data. It's easy enough to instantiate individual <ListItem> components and .push them into an array to render, but I'm having trouble getting the nested items into the node structure on the fly.

The best I've been able to do so far is to work from the bottom up, finding a bottom level of JSON nodes, pushing them into an array, creating the parent component, assigning my previously-created array as children of the parent, etc. e.g.

var Children = [
   <ListItem key={1} primaryText='Child 1' />
   <ListItem key={2} primaryText='Child 2' />
]

then...

var Parents = [
   <ListItem key={0} primaryText='Parent 1' nestedItems={Children} />
]

This is a bit cumbersome; is there a more graceful way to map up parents with nested children into a material-ui <List>?

Upvotes: 5

Views: 10998

Answers (2)

Sergey Reutskiy
Sergey Reutskiy

Reputation: 2811

Imagine that we have an object similar to the following...

RootObject = {
  children: [
    {
      children: [
        {
          children: [ {...props}, {...props}]
        },
        ...props,
      ]
    },
    {
      children: [...],
    },
    ...
    {
      children: [],
    },
  ],
  ...props,
}

Now we need to create a tree node that will call itself recursively:

const mapStructure = (nodes) => {
  if (nodes) {
    return nodes.map(node => (
      <ListItem
        key={node.id}
        primaryText={node.primaryText}
        initiallyOpen //optional
        nestedItems={mapStructure(node.children)}
      />
    ));
  }
};

And finally:

const DynamicNestedItems = ({ RootObject }) => {
  return (
    <List>
      {mapStructure(RootObject)}
    </List>
  );
};

export default DynamicNestedItems;

Upvotes: 7

Luca Faggianelli
Luca Faggianelli

Reputation: 2522

I had the very same problem, the only difference is that I have a menu hierarchy described in XML instead of JSON.

I created a main component ConfigurationNav that you can instantiate with <ConfigurationNav nav={this.state.nav}/>:

var ConfigurationNav = React.createClass({
  render: function() {
    var items = [];
    var _this = this;
    this.props.nav.find('> navitem').each(function(i) {
      items.push(<ConfigurationNavItem item={this} />);
    });

    return (
      <List>
        {items}
      </List>
    );
  }
});

Then there is a subcomponent that creates the ListItems:

var ConfigurationNavItem = React.createClass({
  render: function() {
    var nested = [];
    for (var i=0; i<this.props.item.children.length; i++) {
      nested.push(<ConfigurationNavItem item={this.props.item.children[i]} />);
    }

    return (
      <ListItem key={this.props.item.id}
          primaryText={this.props.item.getAttribute('label')}
          nestedItems={nested}
          />
    );
  }
});

This approach works for me, but somehow I lost the indent of the nested items, still working on it.

Luca

Upvotes: 1

Related Questions