需求
angular-ui bootstrap提供了很多重写过bootstrap的组件,利用的就是AngularJS自定义指令directive。但是其实,很多组件都已经有别的框架写好了,比如jQuery的jquery-ui等等。所以,我有个想法,能不能利用这些已经存在组件,组装一个适配AngularJS的控件呢。因此做如下尝试,将bootstrap-treeview做一个适配AngularJS的,达到预期效果。
利用到的框架
bootstrap-treeview简介
bootstrap-treeview是一个jquery的插件组件,是个树状结构数据展示组件,树结构的样式依赖bootstrap,官方的截图如下:
bootstrap-treeviewbootstrap-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基本用法
- 页面添加标签
<div id="treeView"></div>
- 创建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>
运行截图
- 初始页面
- 点击Refresh
网友评论