Yii组件CTreeView使用小结

Yii的CTreeView树形图组件是对jQuery插件treeview的封装,所以,要了解该组件的细节,需要直接去查阅jquery-plug-treeview 。从该作者的提示来看,这个插件的开发有点处于停滞状态,不过对于一般的使用,该插件仍然适用。一开始也考虑了zTree插件(也有人进行了简单的Yii封装),但因为现成的CTreeView能满足需求,并且框架本身的组件在兼容性上有保证,所以还是采用了它。


撇开细节,从数据提供者角度来说,生成CTreeView的方式有两种:静态的(设置data属性)或动态的(Ajax,设置url)

每个节点格式为   $node = array('text' =>显示文本, 'id'=>动态加载必须此id, 'hasChildren'=>动态加载必须, 'children'=>非叶子节点需要),节点描述是一个array,父节点由子节点数组构成,所以是array(array(..),...),前面的'children'对应的值就是这样。

静态生成相对简单,只需要在view中写死代码(但需要注意层次)。

<?php

    $this->widget('CTreeView', array(
        'animated' => 'normal',
        'data' => array(
            array(
                'text' => '<span>标题A</span>',
                'children' => array(array(
                        'text' => '<a href="http://127.0.0.1/index.php?r=site/index'或 
                        '"'.$this->createUrl('/site/index').'">标题B</a>',
                        'text' => '<a href="http://127.0.0.1/index.php?r=site/index'或 
                        '"'.$this->createUrl('/site/index').'">标题BB</a>',
                        ), ),
                ),
            array(
                'expanded' => false,
                'text' => '<span>标题C</span>',
                'children' => array(
                    array('text' => '标题D'),
                    array('text' => '标题DD', ),
                    ),
                ),
            ),
        ));

?>

静态生成基本上当导航使用,和直接编辑一个没有特别的优势。

动态生成网上的例子基本上是来源于Yii的论坛的例子,大致做法如下:

先创建一个描述节点父子关系的表

CREATE TABLE `menu` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `id_parent` int(11) DEFAULT NULL,
 `title` varchar(25) COLLATE utf8_unicode_ci NOT NULL,
 `position` int(2) NOT NULL,
 `url` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
 `icon` varchar(50) COLLATE utf8_unicode_ci DEFAULT NULL,
 PRIMARY KEY (`id`),
 KEY `id_parent` (`id_parent`)
) ENGINE=InnoDB 

menu外键约束到自身:id_parent references menu(id)

在view视图中写入:(注意url属性指向 ajaxFillTree,表示数据由该控制器方法提供,代码可以用cookie持久化,通过缓存可以提高性能)

<?php
        $this->widget(
                'CTreeView',
                array(
                        'animated'=>'fast', //quick animation
                        'collapsed' => true,
                        'url' => array('/backOffice/sideMenu/ajaxFillTree'),
                        'htmlOptions'=>array(
                                'class'=>'treeview-famfamfam',
                        )
                )
        );
?>

控制器中添加Ajax特性的方法ajaxFillTree:

        public function actionAjaxFillTree(){
                if (!Yii::app()->request->isAjaxRequest) {
                        exit();
                }
                $parentId = "NULL";
                if (isset($_GET['root']) && $_GET['root'] !== 'source') {
                        $parentId = (int) $_GET['root'];
                }
                $sql = "SELECT m1.id, m1.title AS text, m2.id IS NOT NULL AS hasChildren,m1.url "
                . "FROM menu AS m1 LEFT JOIN menu AS m2 ON m1.id=m2.id_parent "
                . "WHERE m1.id_parent <=> $parentId "
                . "GROUP BY m1.id ORDER BY m1.position ASC";
                $req = Yii::app()->db->createCommand($sql);
                $children = $req->queryAll();

                $children = $this->createLinks($children);

                echo str_replace(
                        '"hasChildren":"0"',
                        '"hasChildren":false',
                        CTreeView::saveDataAsJson($children)
                );
                exit();
        }
        
        private function createLinks($children){
                $child = array();
                $return = array();
                foreach($children AS $key=>$value){
                        $child['id']=$value['id'];
                        $child['text']=$value['text'];
                        $child['hasChildren']=$value['hasChildren'];
                        
                        if(strlen($value['url'])>0){
                                $child['text'] = $this->format($value['text'],$value['url'],Yii::app()->request->url);
                        }
                                
                        $return[] = $child;
                        $child = array();
                }
                
                return $return;
        }
        
        private function format($text, $url,$icon = NULL)
        {
                $img = '';
                if(isset($icon))
                        $img = '<img src="'.app()->theme->baseUrl.'/images/icons/'.$icon.'">';
                
                return sprintf('<span>%s</span>',CHtml::link(($img.' '.$text), app()->createUrl($url)));
        }

但在我的使用场合中,本身表中包含了编码字段,编码是分级的,但存放于同一表格,而且似乎SQL Server不支持安全比较<=>(我猜安全比较是带null的比较),所以,我们将顶级节点id命名为'0'而非null,同时用模型方法来生成数据。

在视图中,我们用:

<?php 
$this->widget('CTreeView', array(
     'persist' => 'cookie',  'cookieId' => 'treeview-zsy001',
     'animated' => 'fast',
     'url' => array('ajaxFillTree'),
     'htmlOptions' => array('id' => 'treeview-zsy001', 'class' => 'treeview-zsy001')
     ));
 ?>

在控制器中:

 

    public function actionAjaxFillTree()
    {
        if (!Yii::app()->request->isAjaxRequest) {
            Yii::app()->end();
        }
        
        $data = Yzsszd::buildCTreeViewData($this->createUrl('admin'), 'tcode');
        
        $parentId = (isset($_GET['root']) && $_GET['root'] != 'source') ? $_GET['root'] : '0';
                    
        echo CTreeView::saveDataAsJson(array_values($data[$parentId]));
        
        Yii::app()->end();

    }

在模型中创建方法来构造CTreeView所需的数据:

    //构建CTreeView用的父子关系表 [父码]=array([子码]=>array子码描述)
    //用法:CTreeView::saveDataAsJson(array_values($data[$parentId]))
    public static function buildCTreeViewData($orgUrl, $key, $op='&', $showCodeInName = true) 
    {
        $data = array();
        $models = Yzsszd::model()->findAll(array('order'=>'bmdiccode asc'));
        foreach($models as $model){
            $url = $orgUrl.$op.$key.'='.$model->bmdiccode;
            //$url =  'javascript:void(0);';
            $text = ($showCodeInName ? $model->bmdiccode : '')."\t".$model->mcdicname;
            $options = array('href'=>"$url", 'id'=>'treenode-'.$model->bmdiccode, 'class'=>'treenode');
            if($model->point == 1){
                $data['0'][$model->bmdiccode] = array('id'=>$model->bmdiccode,  
                    'text'=> CHtml::openTag('a', $options).$text.CHtml::closeTag('a'));
            }else{
                $data[$model->getParentCode(2)][$model->parentCode]['hasChildren'] = true;
                $data[$model->parentCode][$model->bmdiccode] = array('id'=>$model->bmdiccode,
                    'text'=> CHtml::openTag('a', $options).$text.CHtml::closeTag('a'));
            }
        }
        return $data;
    }


在本质上,动态加载是一开始加载顶级下的节点,然后展开某个节点时,动态加载该节点的子节点,点击展开某个节点时,会向url指向的方法GET方式传递$_GET['root'],对于顶级来说,该值固定为'source',对于被点击的节点,该值为对应节点的id 。每次加载的数据,就是 子节点列表 array(array(...)...),点击节点后,就把一个array(array(...)...)嵌套到前一个结构,所以,动态加载的过程就是分步生成静态加载时的树形数据的过程。


但在我们的例子中,结果绑定到节点的链接会导致页面跳转,并且尝试把链接修改成CHtml::ajaxLink无效。后来查阅了以下帖子:http://blog.csdn.net/qqamoon/article/details/6315216

才知道动态生成的元素会无法响应一般方法定义的用户事件,然后知道用

$(document).on('click''元素',function () {  alert(this.hash);  });    或者  bind()函数可以解决该问题

所以,在视图添加javascript注册:

<?php

Yii::app()->clientScript->registerScript('**注册名***', "

$(document).on('click', 'a.treenode', function(){
    alert($(this).attr('href'));
    jQuery('#zsy001zj1-grid').yiiGridView('update', {
        url : $(this).attr('href')
    });
    return false;
});

");

?>

后来发现,Yii框架生成的Javascript基本上使用了上述方式,例如,CGridView对应的Javascript代码


 









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值