Ember-嵌套递归模块

时间:2018-09-26 09:51:06

标签: javascript recursion ember.js

我正在使用单击和放下功能---在页面上以递归方式使用该模块,因此该模块具有父级和子级。

我遇到了一个问题,如果用户开始选择子级-然后选择父级-我想取消选择子级。尽管我不确定如何存储或监视为进行全局取消选择而选择的父项/子项的更改。

因此,用户已选择bacon3的子级。如果他们选择了父级,则需要取消选择子级,但我觉得我目前已锁定在模块范围内

enter image description here

1 个答案:

答案 0 :(得分:4)

我认为这个示例将帮助您https://canary.ember-twiddle.com/468a737efbbf447966dd83ac734f62ad?openFiles=utils.tree-helpers.js%2C

所以,这是一个有趣的问题。事实证明,这与递归,javascript或复选框行为无关,而更多是递归问题。

这就是我所拥有的(使用更新的语法等(如果您可以选择升级到3.4,则绝对应该-只是梦想))

// wrapping-component.js
import Component from '@ember/component';
import { action, computed } from '@ember-decorators/object';

import { check } from 'twiddle/utils/tree-helpers';

export default class extends Component {  
  options = [{
    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
    }]
  }];

  @action
  toggleChecked(id) {
    const newTree = check(this.options, id);

    this.set('options', newTree);
  }
}

模板:

{{yield this.options (action this.toggleChecked)}}

及其用法:

// application.hbs
<WrappingComponent as |options toggle|>
  {{#each options as |item|}}

    <CheckboxGroup @item={{item}} @onClick={{toggle}} />

  {{/each}}
</WrappingComponent>

CheckboxGroup是仅模板的组件:

// checkbox-group.hbs
<div class="checkboxhandler">
  <input 
    type="checkbox" 
    checked={{@item.checked}}
    onclick={{action @onClick @item.id}}
  >
  <label>{{@item.label}}</label>

  {{#if @item.children}}
    {{#each @item.children as |child|}}

       <CheckboxGroup @item={{child}} @onClick={{@onClick}} />

    {{/each}}
  {{/if}}
</div>

和递归助手(这很糟,但是我只是在制作原型):

// utils/tree-helpers.js
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 selectOnlySubtree(tree, 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) {
    return { 
        ...tree, 
        checked: id === 'all' ? transform(tree.checked) : tree.checked,
    children: check(tree.children, id, transform) 
  };
}

export function didChange(treeA, treeB) {
  const rootsChanged = treeA.checked !== treeB.checked;

  if (rootsChanged) return true;

  if (treeA.children && treeB.children) {
    const compares = treeA.children.map((childA, index) => {
      return didChange(childA, treeB.children[index]);
    });

    const nothingChanged = compares.every(v => v === false);

    return !nothingChanged;
  }

  return false;
}

希望这会有所帮助。

enter image description here