#javascript #reactjs #accordion
#javascript #reactjs #аккордеон
Вопрос:
Как я могу создать такой аккордеон
-parent
-subparent1
-subparent2
...
-subparentN
- child
Вот мои данные.
//parents
{id: 1, name: "", parent_id: null}
{id: 2, name: "", parent_id: null }
{id: 3, name: "", parent_id: null }
{id: 4, name: "", parent_id: null}
//children
{id: 5, name: "", parent_id: 1}
{id: 6, name: "", parent_id: 1}
{id: 7, name: "", parent_id: 5}
{id: 8, name: "", parent_id: 5}
{id: 9, name: "", parent_id: 6}
{id: 10, name: "", parent_id: 6}
{id: 11, name: "", parent_id: 6}
{id: 12, name: "", parent_id: 6}
{id: 13,name: "", parent_id: 6}
{id: 14, name: "", parent_id: 6}
В основном те, у кого есть parent_id:null, являются родителями, и когда я нажимаю на них, я хочу, чтобы отображались их потенциальные дочерние элементы, если они у них есть, теперь это не так сложно, но я не могу понять, как отображать дочерние элементы subparent
Комментарии:
1. создайте дочерние дочерние элементы в качестве компонента с собственным состоянием open / close, а затем распространяйте его по щелчку, должно быть так же, как вы делали для parent
Ответ №1:
Вы можете перебрать список всех ваших элементов и добавить каждый подпункт к их родительскому элементу. После этого вам просто нужно перебрать все элементы в массиве и создать их соответствующий html.
const items = [
//parents
{id: 1, name: "1", parent_id: null},
{id: 2, name: "2", parent_id: null },
{id: 3, name: "3", parent_id: null },
{id: 4, name: "4", parent_id: null},
//children
{id: 5, name: "5", parent_id: 1},
{id: 6, name: "6", parent_id: 1},
{id: 7, name: "7", parent_id: 5},
{id: 8, name: "8", parent_id: 5},
{id: 9, name: "9", parent_id: 6},
{id: 10, name: "10", parent_id: 6},
{id: 11, name: "11", parent_id: 6},
{id: 12, name: "12", parent_id: 6},
{id: 13,name: "13", parent_id: 6},
{id: 14, name: "14", parent_id: 6},
];
for(const item of items) {
// Find the parent object
const parent = items.find(({ id }) => id === item.parent_id);
// If the parent is found add the object to its children array
if(parent) {
parent.children = parent.children ? [...parent.children, item] : [item]
}
};
// Only keep root elements (parents) in the main array
const list = items.filter(({ parent_id }) => !parent_id);
// console.log(list);
// Show tree (vanillaJS, no REACT)
for(const item of list) {
// Create a new branch for each item
const ul = createBranch(item);
// Append branch to the document
document.body.appendChild(ul);
}
function createBranch(item) {
// Create ul item for each branch
const ul = document.createElement("ul");
// Add current item as li to the branch
const li = document.createElement("li");
li.textContent = item.name;
ul.appendChild(li);
// Check if there are children
if(item.children) {
// Create a new branch for each child
for(const child of item.children) {
const subUl = createBranch(child);
// Append child branch to current branch
ul.appendChild(subUl);
}
}
return ul;
}
ul {
margin: 0;
padding-left: 2rem;
}
Комментарии:
1. ты абсолютный зверь, настоящий гений, огромное тебе спасибо, ты спасаешь жизнь. Я бы никогда даже близко не подошел к этому сам. Кстати, у меня есть еще один дочерний слой, и он работает все так же, еще раз спасибо!
Ответ №2:
Я думаю, что ваша структура данных должна быть вложенным объектом аналогично тому, как вы видите, как работает ваше меню, т. Е.
[
{id: 1, name:"", children: [
{id: 5, name: "", children: []},
{id: 6, name: "", children: [
{id: 7, name: "", children: []},
{id: 8, name: "", children: []},
]},
]},
{id: 2, name:"", children:[]}
]
Тогда вам понадобится функция для вывода каждого элемента:
const returnMenuItem = (item, i) =>{
let menuItem;
if (item.children.length===0) {
menuItem = <div key={i}>{item.label}</div>;
}
else {
let menuItemChildren = item.children.map((item,i)=>{
let menuItem = returnMenuItem(item,i);
return menuItem;
});
menuItem = <div key={i}>
<div>{item.label}</div>
<div>
{menuItemChildren}
</div>
</div>;
}
return menuItem;
}
И вы бы вызвали эту функцию, просмотрев свои элементы:
let menuItems = data.map((item,i)=>{
let menuItem = returnMenuItem(item,i);
return menuItem;
});
Полный компонент будет выглядеть примерно так:
import React, { useState, useEffect } from "react";
import { UncontrolledCollapse } from "reactstrap";
const Menu = (props) => {
const [loading, setLoading] = useState(true);
const [items, setItems] = useState([]);
useEffect(() => {
const menuData = [
{
id: 1,
name: "test 1",
children: [
{ id: 5, name: "test 5", children: [] },
{
id: 6,
name: "test 6",
children: [
{ id: 7, name: "test 7", children: [] },
{ id: 8, name: "test 8", children: [] }
]
}
]
},
{ id: 2, name: "test 2", children: [] }
];
const returnMenuItem = (item, i) => {
let menuItem;
if (item.children.length === 0) {
menuItem = (
<div className="item" key={i}>
{item.name}
</div>
);
} else {
let menuItemChildren = item.children.map((item, i) => {
let menuItem = returnMenuItem(item, i);
return menuItem;
});
menuItem = (
<div key={i} className="item">
<div className="toggler" id={`toggle-menu-item-${item.id}`}>
{item.name}
</div>
<UncontrolledCollapse
className="children"
toggler={`#toggle-menu-item-${item.id}`}
>
{menuItemChildren}
</UncontrolledCollapse>
</div>
);
}
return menuItem;
};
const load = async () => {
setLoading(false);
let menuItems = menuData.map((item, i) => {
let menuItem = returnMenuItem(item, i);
return menuItem;
});
setItems(menuItems);
};
if (loading) {
load();
}
}, [loading]);
return <div className="items">{items}</div>;
};
export default Menu;
И минимальный css:
.item {
display: block;
}
.item > .children {
padding: 0 0 0 40px;
}
.item > .toggler {
display: inline-block;
}
.item::before {
content: "-";
padding: 0 5px 0 0;
}
Вы можете найти рабочую песочницу кода здесь песочница
Комментарии:
1. привет, отличный ответ, это работает, если я хочу отобразить все элементы, но как бы я сделал что-то вроде аккордеона? Я хочу нажать родительский элемент и заставить дочерние элементы свернуться.
2. Обычно я использую reactstrap в качестве пакета react-bootstrap. Затем я оборачиваю свое меню в навигатор ( reactstrap.github.io/components/navs ) и пункты меню, у которых есть дочерние элементы в элементе UncontrolledDropdown (reactstrap.github.io/components/ dropdowns ). Наконец, для элементов я использую элемент NavItem . Если вы хотите использовать подход аккордеона, вы можете обернуть свои элементы дочерними элементами в collapse ( reactstrap.github.io/components/collapse ). Если вам это сложно или вы не уверены, как это использовать, подробно опишите ожидаемое поведение, и я могу расширить свой ответ, чтобы охватить это.
3. да, пожалуйста. Я не понимаю, как заставить его работать с функцией returnMenuItem, которую вы были так любезны предоставить. Большое спасибо, от всей этой вложенности и рекурсии у меня кружится голова
4. Я обновил свой ответ примером компонента. Теперь у вас должно быть все, что вам нужно, чтобы создать компонент в соответствии с вашими потребностями.
5. Приветствия, всегда рад помочь. Если это то, что вы хотели, тогда вы должны проверить это как правильный ответ.
Ответ №3:
Я думаю, что в вашей структуре данных есть недостаток. Помимо отношения «потомок-родитель», вы должны отслеживать отношения «родитель-потомок». Теперь вы сможете легко перебирать данные и отображать дочерние элементы дочернего элемента.
{id: 1, parent_id: null, children: [
{id: 2, parent_id: 1, children: []},
{id: 3, parent_id: 1, children: [
{id: 4, parent_id: 3, children: []}
]}
]}
Если вам нужно сохранить все объекты встроенными, вы можете структурировать свои данные следующим образом:
{id: 1, parent_id: null, children: [2, 3]}
{id: 2, parent_id: 1, children: []},
{id: 3, parent_id: 1, children: [4]},
{id: 4, parent_id: 3, children: []}