选择同级节点时如何递归取消选择表兄弟节点(递归呈现的复选框) [英] How do I recursively de-select cousin nodes when selecting sibling nodes (recursively rendered checkboxes)

查看:46
本文介绍了选择同级节点时如何递归取消选择表兄弟节点(递归呈现的复选框)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在建立一个多选检查组项目列表。目标是一次仅拥有一个活动组。因此,用户可以选择父项-但是,如果他们选择子项,则该子组将成为焦点,而选中的父项将变为未选中状态。

 < 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. 当前-我可以选择过滤器-然后是辣椒-并且不会取消过滤器检查-因此,这是一个错误

//错误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

  1. currently - I can select chilli - then burger - and no decheck of chilli occurs - so that's a bug
  2. currently - I can select coffee maker - then pickle - and no decheck of coffee maker occurs - so that's a bug
  3. 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 to true
      • Skip the other items
    • If it is not:
      • Set all items to checked: false
  • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆