选择同级节点时如何递归取消选择表兄弟节点(递归呈现的复选框) [英] How do I recursively de-select cousin nodes when selecting sibling nodes (recursively rendered checkboxes)
问题描述
我正在建立一个多选检查组项目列表。目标是一次仅拥有一个活动组。因此,用户可以选择父项-但是,如果他们选择子项,则该子组将成为焦点,而选中的父项将变为未选中状态。
< div class = checkboxhandler>
<输入
type = checkbox
已选中= {{@@ item.checked}}
onclick = {{action @onClick @ item.id}}
>
< label> {{@ item.label}}-已选中:{{@@ item.checked}}< / label>
{{#if @ item.children}}
{{#each @ item.children as | child |}}
< CheckboxGroup @ item = {{child}} @onClick = {{@ onClick}} />
{{/ each}}
{{/ if}}
< / div>
我已经了解了带有递归帮助程序检查的复选框。
这是帮助器树-取消选择的逻辑发生在这里。此应用程序还需要保留selectedItems的数组-但需要清除这些数组以及复选框。
const toggle =值=> !值;
const disable =()=>假;
//根/兄弟姐妹包含在数组中
导出函数check(tree,id,transform = toggle){
if(tree === undefined)返回未定义;
if(Array.isArray(tree)){
return tree.map(t => check(t,id,transform));
}
if(tree.id === id || id ===全部){
return checkNode(tree,id,transform);
}
if(tree.children){
return checkChildren(tree,id,transform);
}
回报树;
}
函数selectOnlySubtree(tree,id,transform){
return tree.map(subTree => {
const newTree = check(subTree,id,转换);
if(!newTree.children ||(转换!==禁用& didChange(newTree,subTree))){
return newTree;
}
return disableTree(subTree);
});
}
函数isTargetAtThisLevel(tree,id){
返回tree.map(t => t.id).includes(id);
}
函数checkNode(tree,id,transform){
return {
... tree,
check:transform(tree.checked) ,
子级:disableTree(tree.children)
};
}
函数disableTree(tree){
return check(tree,'all',disable);
}
函数checkChildren(tree,id,transform){
const newChildren = check(tree.children,id,transform);
const更改= didChange(tree.children,newChildren);
const检查=已更改? false:(
id ==='all'?transform(tree.checked):tree.checked
);
return {
... tree,
check:checked,
children:check(tree.children,id,transform)
};
}
导出函数didChange(treeA,treeB){
const rootsChanged = treeA.checked!== treeB.checked;
如果(rootsChanged)返回true;
if(Array.isArray(treeA)&& Array.isArray(treeB)){
return didChangeList(treeA,treeB);
}
if(treeA.children&& treeB.children){
return didChangeList(treeA.children,treeB.children);
}
返回false;
}
函数didChangeList(a,b){
const compares = a.map((childA,index)= >> {
return didChange(childA, b [index]);
});
const noneChanged = compares.every(v => v === false);
return!nothingChanged;
}
//最新余烬
-仅选中了孩子
,但是当前的错误正在发生
- 目前-我可以选择辣椒-然后是汉堡-而且没有肉馅出现
辣椒的颈部-这是一个错误 - 当前-我可以选择咖啡机-然后选择泡菜-并且不取消检查咖啡机-所以这是一个错误
- 当前-我可以选择过滤器-然后是辣椒-并且不会取消过滤器检查-因此,这是一个错误
//错误1的说明-在这种情况下,只有汉堡应该保持选中状态
/ /错误2的说明-因此在这种情况下,只有腌制应该保持选中状态
//问题3-在这种情况下,只有辣椒应该保持选中状态
在每次 false->之后,您可以true
过渡,请取消选中整个树,除了包括当前正在更新的项目的层。
例如:
- 在图层上循环
- 检查设置为true的项目是否在层使用
.find
- 如果是:
- 将其
已选中
属性设置为true
- 跳过其他项目
- 将其
- 如果不是,则:
- 将所有项目设置为
已选中:false
- 将所有项目设置为
- 如果是:
- 每个具有
子项
数组的项的递归
const turnOn =(options,id)=> {const target = options.find(o => o.id === id);如果(target){target.checked = true; } else {options.forEach(o => {o.checked = false;})} //递归options.forEach(({children = []})=> gt; turnOn(children,id));} var opts = options(); turnOn(opts,3); turnOn(opts,4); console.log(在3& 4之后检查:,getChecked(opts)); turnOn(opts,2); console.log( 2点后检查,getChecked(opts)); turnOn(opts,6); console.log( 6点后检查:,getChecked(opts)); function options(){return [{id:1,label :汉堡,选中:否,孩子:[{id:3,标签: tomato,选中:false},{id:4,标签: lettus,选中:false},{id:5,标签:'pickle',已选中:false}]},{id:2,标签:'kebab',已选中:false,子级:[{id:6,label:'番茄酱',已选中:false},{id:7 ,标签:'chilli',已检查:false}]}];};函数getChecked(opts){返回opts.reduce((acc,o)=> acc.concat(o.checked?o.label:[] ,getChecked(o.children || []) ),[]);}
注意:我带有一个递归函数,该函数仅使树变异而不是返回新树,因为它更易于阅读
I am building a multi-select checkdown group item list. The goal is to have only ONE active group at a time. So the user can select parents - but if they select a child item, that child group becomes the focus and the selected parents become unchecked.
<div class="checkboxhandler">
<input
type="checkbox"
checked={{@item.checked}}
onclick={{action @onClick @item.id}}
>
<label>{{@item.label}} -- checked: {{@item.checked}}</label>
{{#if @item.children}}
{{#each @item.children as |child|}}
<CheckboxGroup @item={{child}} @onClick={{@onClick}} />
{{/each}}
{{/if}}
</div>
I've got as far with the checkboxes with recursive helper checks.
This is the helper tree - where the logic to deselect takes place. This application also needs to hold the array for selectedItems - but needs to clear those array's as well as the checkboxes.
const toggle = value => !value;
const disable = () => false;
// the roots / siblings are contained by arrays
export function check(tree, id, transform = toggle) {
if (tree === undefined) return undefined;
if (Array.isArray(tree)) {
return tree.map(t => check(t, id, transform));
}
if (tree.id === id || id === 'all') {
return checkNode(tree, id, transform);
}
if (tree.children) {
return checkChildren(tree, id, transform);
}
return tree;
}
function selectOnlySubtree(tree, id, transform) {
return tree.map(subTree => {
const newTree = check(subTree, id, transform);
if (!newTree.children || (transform !== disable && didChange(newTree, subTree))) {
return newTree;
}
return disableTree(subTree);
});
}
function isTargetAtThisLevel(tree, id) {
return tree.map(t => t.id).includes(id);
}
function checkNode(tree, id, transform) {
return {
...tree,
checked: transform(tree.checked),
children: disableTree(tree.children)
};
}
function disableTree(tree) {
return check(tree, 'all', disable);
}
function checkChildren(tree, id, transform) {
const newChildren = check(tree.children, id, transform);
const changed = didChange(tree.children, newChildren);
const checked = changed ? false : (
id === 'all' ? transform(tree.checked) : tree.checked
);
return {
...tree,
checked: checked,
children: check(tree.children, id, transform)
};
}
export function didChange(treeA, treeB) {
const rootsChanged = treeA.checked !== treeB.checked;
if (rootsChanged) return true;
if (Array.isArray(treeA) && Array.isArray(treeB)) {
return didChangeList(treeA, treeB);
}
if (treeA.children && treeB.children) {
return didChangeList(treeA.children, treeB.children);
}
return false;
}
function didChangeList(a, b) {
const compares = a.map((childA, index) => {
return didChange(childA, b[index]);
});
const nothingChanged = compares.every(v => v === false);
return !nothingChanged;
}
//latest ember fiddle https://canary.ember-twiddle.com/f28edbf72193427c2a527e51d57e759f?openFiles=components.wrapping-component.js%2Ctemplates.components.checkbox-group.hbs
so these are valid conditions -- just parents selected
-- just children selected
but the current bugs are occuring
- currently - I can select chilli - then burger - and no decheck of chilli occurs - so that's a bug
- currently - I can select coffee maker - then pickle - and no decheck of coffee maker occurs - so that's a bug
- currently - I can select filter - then chilli - and no decheck of filter occurs - so that's a bug
//illustration of error 1 - so only burger should remain selected in this instance
//illustration of error 2 - so only pickle should remain selected in this instance
//illustration of problem 3 - so only chilli should remain selected in this instance
You could, after every false -> true
transition, uncheck the whole tree except the layer that includes the item that's currently being updated.
For example:
- Loop over a layer
- Check if the item that's being set to true is in the layer using
.find
- If it is:
- Set it's
checked
property totrue
- Skip the other items
- Set it's
- If it is not:
- Set all items to
checked: false
- Set all items to
- If it is:
- Recurse for every item with a
children
array
const turnOn = (options, id) => {
const target = options.find(o => o.id === id);
if (target) {
target.checked = true;
} else {
options.forEach(o => { o.checked = false; })
}
// Recurse
options.forEach(({ children = [] }) => turnOn(children, id));
}
var opts = options();
turnOn(opts, 3);
turnOn(opts, 4);
console.log("Checked after 3 & 4:", getChecked(opts));
turnOn(opts, 2);
console.log("Checked after 2:", getChecked(opts));
turnOn(opts, 6);
console.log("Checked after 6:", getChecked(opts));
function options() {
return [{
id: 1,
label: 'burger',
checked: false,
children: [{
id: 3,
label: 'tomato',
checked: false
}, {
id: 4,
label: 'lettus',
checked: false
}, {
id: 5,
label: 'pickle',
checked: false
}]
}, {
id: 2,
label: 'kebab',
checked: false,
children: [{
id: 6,
label: 'ketchup',
checked: false
}, {
id: 7,
label: 'chilli',
checked: false
}]
}];
};
function getChecked(opts) {
return opts.reduce((acc, o) => acc.concat(
o.checked ? o.label : [],
getChecked(o.children || [])
), []);
}
Note: I went with a recursive function that simply mutates the tree rather than returning a new one because it's easier to read
这篇关于选择同级节点时如何递归取消选择表兄弟节点(递归呈现的复选框)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!