2

I am making dynamic menus using a recursive function and I have already made the menus and it display in the right order without any issues.

And I receive the data for menu from service.js file and you can see the entire working application in the below code sandbox example,

https://codesandbox.io/s/reactstrap-accordion-3uoz9

Requirement:

Here I am in the need to find out the last level of menus and need to assign checkbox with value as their respective id {item.id}.

Eg:

For First menu one,

 -> [Checkbox with value as 1.1.1] One-One-One
 -> [Checkbox with value as 1.1.2] One - one - two
 -> [Checkbox with value as 1.1.3] One - one - three

For Second menu two,

 -> [Checkbox with value as 2.1] Two - one

.

.

.

For sixth menu six,

 -> [Checkbox with value as 6] Six

I hope the point is clear that I need to find out the last level in recursion and should assign a checkbox to it with the value of their id.

Please fork the code sandbox provided and help me to achieve the result of making the checkbox at the last level.

Optional requirement: The collapse is working for whole menus at once if possible please make it collapse at each individual level in unique.

But the main important requirement is to make a checkbox at the last level of menus. A big thanks in advance...

Edit:

As commented by Crowder, I have created the snippet removing reactstrap code and it is okay now because I am in the need of displaying checkbox inline to laste level of submenus (last children elements).

const menuData = [
  {
    id: "1",
    name: "One",
    children: [
      {
        id: "1.1",
        name: "One - one",
        children: [
          { id: "1.1.1", name: "One - one - one" },
          { id: "1.1.2", name: "One - one - two" },
          { id: "1.1.3", name: "One - one - three" }
        ]
      }
    ]
  },
  {
    id: "2",
    name: "Two",
    children: [{ id: "2.1", name: "Two - one" }]
  },
  {
    id: "3",
    name: "Three",
    children: [
      {
        id: "3.1",
        name: "Three - one",
        children: [
          {
            id: "3.1.1",
            name: "Three - one - one",
            children: [
              {
                id: "3.1.1.1",
                name: "Three - one - one - one",
                children: [
                  { id: "3.1.1.1.1", name: "Three - one - one - one - one" }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  { id: "4", name: "Four" },
  {
    id: "5",
    name: "Five",
    children: [
      { id: "5.1", name: "Five - one" },
      { id: "5.2", name: "Five - two" },
      { id: "5.3", name: "Five - three" },
      { id: "5.4", name: "Five - four" }
    ]
  },
  { id: "6", name: "Six" }
];

class App extends React.Component {
  constructor(props) {
    super(props);


    this.state = {
      currentSelection: "",
      menuItems: [],
      isToggleOpen: false
    };
  }

  componentDidMount() {
    this.setState({ menuItems: menuData });
  }

  handleClick(id, evt) {
    evt.preventDefault();
    console.log("click handler called with", id);
    this.setState({ currentSelection: id });
  }

  toggle() {
    console.log(this.state);
    this.setState({
      isToggleOpen: !this.state.isToggleOpen
    });
  }

  buildMenu(items) {
    return (
      <ul>
        {items &&
          items.map(item => (
            <li key={item.id}>
              <div>
                {item.name}
                {item.children && item.children.length > 0
                  ? this.buildMenu(item.children)
                  : null}
              </div>
            </li>
          ))}
      </ul>
    );
  }

  render() {
    return (
      <div>
        <h2>Click any of the below option</h2>
        {this.state.menuItems &&
          this.state.menuItems.map((item, index) => {
            return (
              <div key={index}>
                {item.name}
                {this.buildMenu(item.children)}
              </div>
            );
          })}
      </div>
    );
  }
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/4.8.0/reactstrap.min.js"></script>
<link href="https://cdnjs.cloudflare.com/ajax/libs/reactstrap/4.8.0/reactstrap.min.css" />
<div id="root"></div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.development.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.development.js"></script>
  • 1
    @T.J.Crowder, If I am not wrong this is how react OP's will post the question and provide a link to code sandbox that is what I see maximum in Stack Overflow.. Even for angular also they provide Stackblitz link.. Even people ask for minum reproducible link in such link only. mainly in react. –  Mar 09 '20 at 12:06
  • I am not sure who has downvoted eventhough my question is very clear and I have tried maximum attempts.. –  Mar 09 '20 at 12:07
  • **Where** do you need to get that `id`? I can't tell looking at the question where you're planning to use it. – T.J. Crowder Mar 09 '20 at 12:10
  • @T.J.Crowder, Say I am using reactstrap (a react bootstrap) library for collapsible menu will that still work in snippet here?? I have tried but it doesn't.. Anyhow I will post the snippet as like you said.. For ```id``` it is available in ```service.js``` file in the example link provided .. Unless you click on that link you won't get idea but as you said I will try to reproduce the snippet here but I am not sure of calling ```service.js``` file inside ```index.js``` file here.. But let me try.. –  Mar 09 '20 at 12:15
  • 1
    @T.J.Crowder, I am unable to find the way to create two different files such ```index.js``` and ```service.js``` in the code snippet as it allows only one file at a time.. May I get your help from you to do it.. –  Mar 09 '20 at 12:18
  • @T.J.Crowder, I have created the things in a single file but I am getting an error as ```"message": "Uncaught ReferenceError: render is not defined",``` .. Please help me to achieve the result Crowder.. I have provided the array in which I am trying to do the recursion.. –  Mar 09 '20 at 12:38
  • I've fixed a couple of basic errors (it's `ReactDOM.render`, not just `render`, as you could see from the examples I linked to twice above; and you put `menuData` in your constructor, but then referred to it elsewhere), but the snippet still has irrelevancies in it that prevent it running (such as `Button`). I'm afraid i'm out for the afternoon so won't be able to help. FWIW, though, here's a sketch of how `buildMenu` could return the bottom-level `id`: https://pastebin.com/YkQHrZY9 – T.J. Crowder Mar 09 '20 at 12:48
  • 1
    @T.J.Crowder, I have made a simple snippet of actually of what I am in the need.. So can you help me now.. Before the last level I am in the need to assign checkbox with respective id as value.. –  Mar 09 '20 at 13:23
  • @T.J.Crowder, Could you please help me here https://stackoverflow.com/questions/60616336/open-the-collapsible-menu-by-default-based-on-the-id where I have used only snippet of SO.. –  Mar 10 '20 at 12:55

1 Answers1

0
buildMenu(items) {
    return (
      <ul>
        {items &&
          items.map((item, index) => (
            <li key={item.id}>
              <div>
                {this.state.isToggleOpen}
                {(item.children) ?  'Not Last': 'Here you can apply your check box'} //this check if item have children 
                <Button onClick={this.toggle.bind(this)}> {item.name} {index} </Button>

                <Collapse isOpen={this.state.isToggleOpen}>
                  {item.children && item.children.length > 0
                    ? this.buildMenu(item.children, index)
                    : null}
                </Collapse>
              </div>
            </li>
          ))}
      </ul>
    );
  }

Now second case on render

<Button onClick={this.toggle.bind(this)}> {item.name}</Button>

Check if item have children

{(item.children) ? this.buildMenu(item.children) : 'Apply your checkbox here'}

Full Code

import React from "react";
import { render } from "react-dom";
import { loadMenu } from "./service";
import { Button, Collapse } from "reactstrap";

// const buildMenu = function buildMenu(items)

// const Menu = ({ items }) => buildMenu(items);

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      currentSelection: "",
      menuItems: [],
      isToggleOpen: false
    };
  }

  componentDidMount() {
    loadMenu().then(items => this.setState({ menuItems: items }));
  }

  handleClick(id, evt) {
    evt.preventDefault();
    console.log("click handler called with", id);
    this.setState({ currentSelection: id });
  }

  toggle() {
    console.log(this.state);
    this.setState({
      isToggleOpen: !this.state.isToggleOpen
    });
  }

  buildMenu(items) {
    return (
      <ul>
        {items &&
          items.map(item => (
            <li key={item.id}>
              <div>
                {this.state.isToggleOpen}
                {(item.children) ?  'Not Last': 'Here you can apply your check box'}
                <Button onClick={this.toggle.bind(this)}> {item.name} </Button>
                <Collapse isOpen={this.state.isToggleOpen}>
                  {item.children && item.children.length > 0
                    ? this.buildMenu(item.children)
                    : null}
                </Collapse>
              </div>
            </li>
          ))}
      </ul>
    );
  }

  render() {
    console.log(this.state.menuItems);
    return (
      <div>
        <h2>Click any of the below option</h2>
        {this.state.menuItems &&
          this.state.menuItems.map((item, index) => {
            return (
              <div>
                <Button onClick={this.toggle.bind(this)}> {item.name} </Button>
                {(item.children) ?  'Not Last': 'Here you can apply your check box'}
                <Collapse isOpen={this.state.isToggleOpen}>
                  {this.buildMenu(item.children)}
                </Collapse>
              </div>
            );
          })}
      </div>
    );
  }
}

render(<App />, document.getElementById("root"));
sourav satyam
  • 980
  • 5
  • 11
  • Let me know the reason for down voting my answer as {(item.children) ? 'Not Last': 'Here you can apply your check box'} will check if item have any further children and if there isn't then user can apply checkbox. Just downvoting without reason isn't a way. If there is anything wrong I will appreciate the downvote. Whosoever downvoted. – sourav satyam Mar 09 '20 at 12:26
  • @TestUser I haven't added the checkbox but you can get the last element using the code. Just replace your buildMenu method with the upper one. and add {(item.children) ? this.buildMenu(item.children) : 'Apply your checkbox here'} in the – sourav satyam Mar 09 '20 at 12:40
  • It doesn't work for first level only for eg.., ```four``` and ```six```.. –  Mar 09 '20 at 12:45
  • I am in the need to assign check box with respective to the last item as like I have given in question ```[Checkbox with value as 1.1.1] One-One-One``` checkbox at left and the respective text at right with checkbox value as ```id``` but unfortunately your solution doesn't meet my need.. Anyhow your help and time is much appreciated.. –  Mar 09 '20 at 13:13