ecomfe/esui

为啥没有CheckboxTree这样常用的控件?

Closed this issue · 17 comments

为啥没有CheckboxTree这样常用的控件?

Tree本身有select功能,然后某个皮肤实现了选中项是checkbox的样式

在 2015年9月7日,上午12:32,acelan86 notifications@github.com 写道:


Reply to this email directly or view it on GitHub.

@otakustay 程序员都是夜猫子啊~
我之前简单看了下,selectNode没有递归选中子节点跟更新父节点的选中状态,stragry里面也没看到,我能求个例子么?
自己写的CheckboxTree继承Tree样式都要重新写,因为ui-type变了

define(function (require) {
    var Tree = require('esui/Tree');
    var lib = require('esui/lib');

    function CheckboxTree() {
        Tree.apply(this, arguments);
    }

    CheckboxTree.prototype = {
        constructor: CheckboxTree,

        type: 'CheckboxTree',

        getItemHTML: function (node) {
            var data = {
                id: lib.encodeHTML(node.id),
                text: lib.encodeHTML(node.text)
            };
            return lib.format(
                '<input type="checkbox"/><span title="${text}">${text}</span>',
                data
            );
        },
        dispose : function () {
            helper.beforeDispose(this);
            helper.dispose(this);
            helper.afterDispose(this);
        },
        .... some other method
    };

    lib.inherits(CheckboxTree, Tree);
    require('esui/main').register(CheckboxTree);

    return CheckboxTree;
});

递归选中的功能确实没有,因为有些系统“选中节点”“和选中所有子节点”是不等价的

样式上我明天找找,3.2是肯定有的,3.1不记得了……

在 2015年9月7日,上午12:40,acelan86 notifications@github.com 写道:

@otakustay 程序员都是夜猫子啊~
我之前简单看了下,selectNode没有递归选中子节点跟更新父节点的选中状态,stragry里面也没看到,我能求个例子么?
自己写的CheckboxTree继承Tree样式都要重新写,因为ui-type变了

define(function (require) {
var Tree = require('esui/Tree');
var lib = require('esui/lib');

function CheckboxTree() {
    Tree.apply(this, arguments);
}

CheckboxTree.prototype = {
    constructor: CheckboxTree,

    type: 'CheckboxTree',

    getItemHTML: function (node) {
        var data = {
            id: lib.encodeHTML(node.id),
            text: lib.encodeHTML(node.text)
        };
        return lib.format(
            '<input type="checkbox"/><span title="${text}">${text}</span>',
            data
        );
    },
    dispose : function () {
        helper.beforeDispose(this);
        helper.dispose(this);
        helper.afterDispose(this);
    },
    .... some other method
};

lib.inherits(CheckboxTree, Tree);
require('esui/main').register(CheckboxTree);

return CheckboxTree;

});

Reply to this email directly or view it on GitHub.

@otakustay 谢灰大百忙抽空,还是希望能有个专门的这个ui,个人觉得挺常用的(估计项目业务原因 T_T),没有我就写个了。。

另外,上面还暴露了一个问题,ui的对象能继承,但是style没法继承。。这点怎么折腾?

你需要 prototype.styleType = 'Tre e'这个?

在 2015年9月7日,上午12:58,acelan86 notifications@github.com 写道:

@otakustay 谢灰大百忙抽空,还是希望能有个专门的这个ui,个人觉得挺常用的(估计项目业务原因 T_T),没有我就写个了。。

另外,上面还暴露了一个问题,ui的对象能继承,但是style没法继承。。这点怎么折腾?


Reply to this email directly or view it on GitHub.

是!高端,看来是我使用的姿势不对。我去改了,谢谢

http://ecomfe.github.io/esui-family/controls/index.html?control=Tree

这里有个样式,不过确实没有递归选中,递归选中需要自己写一个TreeStrategyenableSelectStrategy中实现

ok~~已经很好了

define(function (require) {
    var Tree = require('esui/Tree');
    var lib = require('esui/lib');
    var helper = require('esui/controlHelper');

    var TreeStrategy = require('esui/TreeStrategy');

    /**
     * 获取checkbox对应的策略树
     */
    function getTreeStrategy() {
        var strategy = new TreeStrategy({
            defaultExpand: 1
        });

        strategy.enableSelectStrategy = function (tree) {
            tree.on(
                'select',
                function (e) {
                    this.selectNode(e.node.id);
                }
            );

            tree.on(
                'unselect',
                function (e) {
                    tree.unselectNode(e.node.id);
                }
            );
        };

        return strategy;
    }

    /**
     * 获取部件的css class
     */
    function getClass(tree, part) {
        return helper.getPartClasses(tree, part).join(' ');
    }

    /**
     * 获取部件的id
     */
    function getId(tree, part) {
        return helper.getId(tree, part);
    }


    function CheckboxTree() {
        Tree.apply(this, arguments);
    }

    CheckboxTree.prototype = {
        constructor: CheckboxTree,

        type: 'CheckboxTree',

        styleType: 'Tree',

        getCategory: function () {
            return 'input';
        },

        initOptions: function (options) {
            lib.extend(options, {
                strategy: getTreeStrategy(),
                selectMode: 'multi'
            });
            Tree.prototype.initOptions.call(this, options);
        },

        //修改节点展现
        getItemHTML: function (node) {
            return lib.format(
                '<input type="checkbox" id="${cbId}"><span>${text}</span>',
                {
                    cbId: getId(this, 'cb-' + node.id),
                    text: lib.encodeHTML(node.text)
                }
            );
        },

        //重写选中节点逻辑,增加checkbox选中和递归选中
        selectNode: function (id, silent, dontRecurse) {
            Tree.prototype.selectNode.call(this, id, silent);
            lib.g(getId(this, 'cb-' + id)).checked = true;

            if (!dontRecurse) {
                this.updateChildSelect(id, true, true);
                this.updateParentSelect(id, true);
            }
        },

        //重写取消选中逻辑, 增加checkbox取消选中和递归取消
        unselectNode: function (id, silent, dontRecurse) {
            Tree.prototype.unselectNode.call(this, id, silent);
            lib.g(getId(this, 'cb-' + id)).checked = false;

            if (!dontRecurse) {
                this.updateChildSelect(id, false, true);
                this.updateParentSelect(id, true);
            }
        },

        //向上检查是否需要取消父节点选中或者选中
        updateParentSelect: function (id) { 
            var nodeData,
                parentId,
                parentData;

            if (
                (nodeData = this.nodeIndex[id]) && 
                (parentId = nodeData.parentId) &&
                (parentData = this.nodeIndex[parentId])
            ) {
                children = parentData.children;
                childLen = children instanceof Array && children.length;
                checked = true;
                count = childLen;
                len = childLen;

                while (childLen--) {
                    //节点是否为选中
                    if (!this.selectedNodeIndex[children[childLen].id]) {
                        count--;
                        checked = false;
                    }
                }


                if (count && !checked) {
                    this.unselectNode(parentId, true, true);
                } else {
                    checked
                        ? this.selectNode(parentId, true, true)
                        : this.unselectNode(parentId, true, true);
                }

            }

            parentId && this.updateParentSelect(parentId);
        },

        //向下检查是否需要取消子节点选中或者选中
        updateChildSelect: function (id, checked) {
            var me = this;
            var nodeData = this.nodeIndex[id];
            var children = nodeData.children;
            var childLen = children instanceof Array && children.length;
            var data;

            if (childLen) {
                while (childLen--) {
                    data = children[childLen];
                    checked
                        ? this.selectNode(data.id, true)
                        : this.unselectNode(data.id, true);
                }
            }
        },

        dispose : function () {
            helper.beforeDispose(this);
            helper.dispose(this);
            helper.afterDispose(this);
        }
    };

    lib.inherits(CheckboxTree, Tree);
    require('esui/main').register(CheckboxTree);

    return CheckboxTree;
});

silent那块还没决定好怎么处理,先直接全部禁掉了

重开下,两个问题:
1、我觉得CheckboxTree应该属于InputControl,应该继承自InputControl和Tree, 这个该怎么写,能多继承么,ui
2、CheckboxTree的fire事件的参数应该是啥?当前节点还是包含被动递归选中的所有节点,就是selectedNodes里面的东西

prototype.getCategory = function () {
    return 'input';
}

@otakustay 灰大,我又来了,还是递归选中的问题

如果我调用tree的selectNode方法去选中一个节点,这时候会触发selectionchange事件,但是这时候我的递归还没完成,所以selectionchange事件里面getSelectedNodes获取不到所有被选中的nodes
这样的情况怎么处理好?

如果我先做递归,然后在执行selectNode方法,那么我在递归中需要判断

//节点是否为选中
if (!this.selectedNodeIndex[children[childLen].id]) {

的处理又不能正确判断了

如果我自己做一套选中index的缓存,不用tree自己的,又感觉挺不爽的。。所以。。

CheckboxTree的代码在上面的讨论中已经给出了

.....我用这样来解决,有点无力....

       /**
         * 为了不改变Tree的代码,将内部方法重新实现一遍
         * 用于后面进行数据管理
         */
        removeSelectedNode: function (node) {
            if (this.selectedNodeIndex[node.id]) {
                delete this.selectedNodeIndex[node.id];
                for (var i = 0; i < this.selectedNodes.length; i++) {
                    if (this.selectedNodes[i] === node) {
                        this.selectedNodes.splice(i, 1);
                    }
                }
                return true;
            }
            return false;
        },


        //重写选中节点逻辑,增加checkbox选中和递归选中
        selectNode: function (id, silent, dontRecurse) {
            var node = this.nodeIndex[id];
            //先写入
            this.addSelectedNode(node);
            lib.g(getId(this, 'cb-' + id)).checked = true;

            if (!dontRecurse) {
                this.updateChildSelect(id, true, true);
                this.updateParentSelect(id, true);
            }
            //在删除,让后面可以触发
            this.removeSelectedNode(node);

            Tree.prototype.selectNode.call(this, id, silent);
        },

        //重写取消选中逻辑, 增加checkbox取消选中和递归取消
        unselectNode: function (id, silent, dontRecurse) {
            var node = this.nodeIndex[id];
            //先删除
            this.removeSelectedNode(node);
            lib.g(getId(this, 'cb-' + id)).checked = false;

            if (!dontRecurse) {
                this.updateChildSelect(id, false, true);
                this.updateParentSelect(id, true);
            }
            //再写入,让后面可以触发
            this.addSelectedNode(node);

            Tree.prototype.unselectNode.call(this, id, silent);
        },

看上去似乎应该你递归时先silent不触发事件,全部完成后手动触发一次selectionchange事件?

是的。。就是比如unselectNode的时候

            //先删除
            this.removeSelectedNode(node);
            ...//递归
            //再写入,让后面可以触发
            this.addSelectedNode(node);

这里比较没办法,如果不先从数据上删除,后面递归无法正确执行,如果数据删除后不重新写入(恢复),后面的事件无法触发,只能想到这么做了,在不修改Tree的前提下,不知道是否有更好的方法,或者需要对Tree的设计进行重新思考

我感觉直接调用fire触发事件就好了啊……

好。。像。。。是。。。的。。。。