美文网首页让前端飞
AnguarJS directive调用外部函数

AnguarJS directive调用外部函数

作者: 外大街 | 来源:发表于2017-03-21 19:46 被阅读0次

    需求

    angular-ui bootstrap提供了很多重写过bootstrap的组件,利用的就是AngularJS自定义指令directive。但是其实,很多组件都已经有别的框架写好了,比如jQuery的jquery-ui等等。所以,我有个想法,能不能利用这些已经存在组件,组装一个适配AngularJS的控件呢。因此做如下尝试,将bootstrap-treeview做一个适配AngularJS的,达到预期效果。

    利用到的框架

    1. requirejs
    2. jquery
    3. bootstrap
    4. bootstrap-treeview

    bootstrap-treeview简介

    bootstrap-treeview是一个jquery的插件组件,是个树状结构数据展示组件,树结构的样式依赖bootstrap,官方的截图如下:

    bootstrap-treeview

    bootstrap-treeview 参数配置

    {
        enableLinks: true, // 树节点是否可以链接,即生成一个a标签
        showTags: true,    // 树节点最右边是否显示badge
        data: []           // 数据源,必须为一个数组
    }
    

    bootstrap-treeview 数据源格式

    data: [{
        text: 'node',
        href: 'javascript:void(0)',  // enableLinks === false 不起作用
        tags: [10],                  // tags 可以不需要,showTags === false 不起作用
        nodes: [
            // 子节点,与父节点格式一致,如果没有子节点,可以为空值
            {
                text: 'subnode',
                href: '#/nodes.json',
            }
            // ......
        ]
    }]
    

    bootstrap-treeview基本用法

    1. 页面添加标签
    <div id="treeView"></div>
    
    1. 创建treeview组件
    $(function () {
        var data = [{
            text: 'parent',
            href: 'javascript:void(0);',
            tags: [2],
            nodes: [{
                text: 'child1',
                href: '#/children/1.json',
            }, {
                text: 'child2',
                href: '#/children/2.json',
            }]
    
        }]
        var options = {
            enableLinks: true,
            showTags: true,
            data: data
        };
    
        $('div#treeView').treeview(options);
    })
    

    显示效果如下图所示:

    treeview示例

    适配方法添加

    因为 bootstrap-treeview 只能在初始化的时候,设置数据源,因此给bootstrap-treeview加入一个方法 setData,具体实现如下:

    Tree.prototype.setData = function(data) {
        this.options.data = data;
        this.init(this.options);
    }
    

    再在Tree函数返回值中增加一个属性:

    setData: $.proxy(this.setData, this)
    

    这样,便给 bootstrap-treeview 的 Tree 增加了一个新方法,其实很简单。

    AngularJS directive指令适配代码实现

    有了对bootstrap-treeview基本的用法了解,现在就可以写我们适配的指令了。这里,我们实现一个适配如下树形结构的数据源,多级代理商结构:

    [{
        "id": 1,
        "name": "一级代理商",
        "agents": [{
            "id": 2,
            "name": "二级代理商",
            "agents": [{
                "id": 5,
                "name": "三级代理商"
            }]
        }]
    }, {
        "id": 3,
        "name": "二级代理商",
        "agents": [{
                "id": 4,
                "name": "三级代理商"
            }, {
                "id": 6,
                "name": "三级代理商"
            },
            {
                "id": 7,
                "name": "终端代理商"
            }
        ]
    }]
    

    代理商数据源中并没有href属性,可以在我们适配的过程中,动态生成。

    主模块 app.moduel.js

    定义一个模块 app

    define([
        'require',
        'angular'
    ], function(require, angular) {
        'use strict';
        return angular.module('app', []);
    });
    

    定义指令 treeview.directive.js

    define([
        'require',
        'app.module'
    ], function(require, app) {
        'use strict';
    
        app.directive('treeView', TreeView);
    
        function TreeView() {
            return {
                template: '<div class="alert alert-warning"></div>',
                scope: {
                    items: '=', // 节点数据源,可以是自己的原始数据结构,有 adpater 函数进行适配
                    adapter: '&' // 适配函数,这是实现适配的关键
                },
                replace: true,
                link: linkFn
            };
        }
        // 链接函数
        function linkFn(scope, element, attrs) {
            // 从scope当中取出已经被 AngularJS 帮我们解析过的适配函数
            // 注意不是 scope.adapter,因为经过AngularJS解析过后,
            // scope的adapter属性只是,我们真正传入的函数表达式的一个代理,
            // 通过这个代理函数的调用,才能得到真正的函数本身
            var adapterFn = scope.adapter();
            // 创建树
            element.treeview({
                enableLinks: true,
                showTags: true,
                data: []
            });
            // 用于缓存数据源,数据未变化,不要去渲染
            var cachedItems = [];
    
            // 监听 items 属性的变化,如果变化了,就将数据源更新到 bootstrap-treeview 中
            scope.$watch('items', function(items) {
                // 如果数据源为变化,直接返回
                if (angular.equals(cachedItems, items)) return;
                // 缓存变化数据
                cachedItems = items;
                // 存放适配之后的数据源
                var nodes = [];
                // 将数据源中的数据,逐条进行适配
                angular.forEach(items, function(item) {
                    // 调用外部适配函数,将适配只收的节点存放近 nodes 数组中
                    adapterFn.call(scope, nodes, item);
                });
                // bootstrap-treeview 本身没有提供 setData 函数,为能够动态设置数据源,我加入的
                element.treeview(true).setData(nodes);
            });
        }
        // 这里无意义,因为 requirejs 要求 define 函数返回一个对象,也可以不返回
        return {};
    });
    

    定义控制器 agents.controller.js

    define([
        'require',
        'app.module',
        'treeview'
    ], function(require, app) {
        'use strict';
    
        app.controller('AgentCtrl', ['$scope', '$http', AgentCtrl]);
    
        function AgentCtrl($scope, $http) {
            $scope.list = list;
            $scope.agentAdapter = agentAdapter;
    
            // 查询代理商
            // list();
    
            function list() {
                $http.get('/raws/agents.json').then(function(response) {
                    var _root = {
                        id: 0,
                        name: '根节点',
                        agents: response.data
                    };
    
                    var agents = [_root];
                    $scope.agents = agents;
                }, function(err) {
                    console.error(err);
                })
            }
    
            // 多级代理商适配函数
            function agentAdapter(nodes, agent) {
                var node = {};
                node.text = agent.name;
                // 根据代理商的属性,动态生成 href 属性
                // 如果是根节点,不能点击
                if (agent.id === 0) {
                    node.href = 'javascript:void(0);';
                } else {
                    node.href = '/agents/' + agent.id + '.json';
                }
    
                nodes.push(node);
    
                if (agent.agents && agent.agents.length > 0) {
                    node.tags = [agent.agents.length];
                    // 生成子节点
                    node.nodes = [];
    
                    // 遍历下级代理商适配子节点
                    angular.forEach(agent.agents, function(agent) {
                        agentAdapter(node.nodes, agent);
                    });
                }
            }
        }
    
        return {};
    });
    

    页面 index.html

    <!DOCTYPE html>
    <html lang="zh" ng-cloak>
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <meta http-equiv="X-UA-Compatible" content="ie=edge">
        <title>TreeView Directive Demo</title>
    
        <base href="/" target="_self" />
    
        <link rel="stylesheet" href="stylesheets/bootstrap.css" />
        <link rel="stylesheet" href="stylesheets/angular-csp.css" />
    
        <script data-main="javascripts/main.js" src="javascripts/require.js" type="text/javascript"></script>
    </head>
    
    <body>
        <div class="container" ng-controller="AgentCtrl">
            <div>
                <button class="btn btn-warning" ng-click="list()"><i class="glyphicon glyphicon-refresh">Refresh</i></button>
            </div>
            <div id="treeView" tree-view items="agents" adapter="agentAdapter" class="alert" style="width: 250px;"></div>
            <!--<tree-view tree-view items="agents" adapter="agentAdapter" class="alert" style="width: 250px;"> </tree-view>-->
        </div>
    </body>
    
    </html>
    

    运行截图

    1. 初始页面
    页面初始状态
    1. 点击Refresh
    加载数据源之后

    项目完整代码

    http://git.oschina.net/dzhang/treeview

    相关文章

      网友评论

        本文标题:AnguarJS directive调用外部函数

        本文链接:https://www.haomeiwen.com/subject/ypqgnttx.html