本章涵盖了高级组件,比如 tree 和 data view。它将为读者呈现一个示例项目为 图片浏览器,它使用 tree 和 data view 组件。以下是本章将要讨论的主题:
- Trees
- Data views
- 拖放
- 图片浏览器 — 一个示例项目
本章的主要目标是探索 tree panel 和 data view 并且使用他们来构建一个示例项目图片浏览器。图片浏览器的最终展示效果如下图。
这个项目中的最重要的组件是 tree panel 和 data view 。本项目中使用的组件和概念有:
- tree panel
- Data views
- Model
- store 和 rest 代理
- 容器和布局
- 引用
- 事件处理
- 过滤
除了 tree panel 和 data view 你已经在之前的章节中学习了所有的我们目前已用到的知识。所以在本章中,我们首先学习 tree panel 和 data view。

tree panel
这在 ExtJS 中是一个非常强大且常用的组件,你可以使用它构建任意类型的树。一个 tree panel 是一个树形结构的具有层次化数据的UI。
它和 Ext.grid.Panel 相似, Ext.tree.Panel 也继承自Ext.panel.Table 。所以,它也是支持多列的。
和 grid panel 不同的是,tree panel 需要一个 tree store (Ext.Store.TreeStore)。 tree store 具有一些 tree panel 的功能所需使用的特殊的属性。
基本的 tree
我们来用一个简单的例子演示。tree panel 至少需要一个 tree store 来提供数据。我们首先来创建 tree store 并硬编码内置数据:
-
var store = Ext.create('Ext.data.TreeStore', { -
root: { -
expanded: true, -
text: 'Continents', -
children: [{ -
text: 'Antarctica', -
leaf: true -
}, { -
text: 'South America', -
expanded: true, -
children: [{ -
text: 'Brazil', -
leaf: true -
}, { -
text: 'Chile', -
leaf: true -
}] -
}, { -
text: 'Asia', -
expanded: true, -
children: [{ -
text: 'India', -
leaf: true -
},{ -
text: 'China', -
leaf: true -
}] -
}, { -
text: 'Africa', -
leaf: true -
}] -
} -
});
接着继续创建 Ext.tree.Panel :
-
Ext.create('Ext.tree.Panel', { -
title: 'Basic Tree', -
width: 200, -
height: 450, -
store: store, -
rootVisible: true, -
renderTo: Ext.getBody() -
});
下列截图为以上代码的输出结果:

现在,我们创建一个高级点的树,它是可以拖拽的。同时还需要用到 tree panel 和 tree store 的一些额外选项。拖拽只需要添加一个插件叫做 treeviewdragdrop 。如以下代码所示:
-
var store = Ext.create('Ext.data.TreeStore', { -
root: { -
expanded: true, -
text: 'Continents', -
checked: false, -
children: [{ -
text: 'Antarctica', -
leaf: true , -
checked: false -
},{ -
text: 'South America', -
expanded: false, -
checked: true, -
children: [{ -
text: 'Chile', -
leaf: true, -
checked: true -
}] -
},{ -
text: 'Asia', -
expanded: true, -
checked: true, -
children: [{ -
text: 'India', -
leaf: true, -
checked: true -
},{ -
text: 'China', -
leaf: true, -
checked: true -
}] -
},{ -
text: 'Africa', -
leaf: true, -
checked: true -
}] -
} -
}); -
Ext.create('Ext.tree.Panel', { -
title: 'Basic Tree', -
width: 200, -
height: 450, -
store: store, -
rootVisible: true, -
useArrows: true, -
lines: false, -
renderTo: Ext.getBody(), -
viewConfig: { -
plugins: { -
ptype: 'treeviewdragdrop', -
containerScroll: true -
} -
} -
});
如以下截图所示的输出。我把节点 South America 拖拽至 Asia 节点之下:

tree grid
你可以将多个列添加到 tree ,同时也能创建 tree grid 。默认 tree 包含一列,用的是 tree store 中节点的文本字段。
在这个 store 中,你可以看到在每个节点上除了节点名称,还添加了一些其他的字段,这些字段用于在 tree panel 的列展示上。tree grid 的功能有例如 列调整,排序,过滤等等,以下是代码:
-
var store = Ext.create('Ext.data.TreeStore', { -
root: { -
expanded: true, -
text: 'Continents', -
children: [{ -
name: 'Antarctica', -
population: 0, -
area: 14, -
leaf: true -
},{ -
name: 'South America', -
population: 385 , -
area: 17.84, -
expanded: false, -
children: [{ -
name: 'Chile', -
population: 18, -
area: 0.7, -
leaf: true, -
}] -
},{ -
name: 'Asia', -
expanded: true, -
population: 4164, -
area: 44.57, -
children: [{ -
name: 'India', -
leaf: true, -
population: 1210, -
area: 3.2 -
},{ -
name: 'China', -
leaf: true, -
population: 1357, -
area: 9.5 -
}] -
},{ -
name: 'Africa', -
leaf: true, -
population: 1110, -
area: 30 -
}] -
} -
});
以下的 grid 和上面的 tree panel 差不多一样,只是添加为多列了,这个 xtyp treecolumn 提供缩进和文件夹结构。像一个正常的 grid 一样,tree grid 的列可以是任意类型的例如 checkbox,picture,button,URL 等等。
默认列大小是可调整的,如果需要你也可以固定它的宽度。看下面的代码:
-
Ext.create('Ext.tree.Panel', { -
title: 'Tree Grid', -
width: 500, -
height: 450, -
store: store, -
rootVisible: false, -
useArrows: true, -
lines: false, -
scope: this, -
renderTo: Ext.getBody(), -
columns: [{ -
xtype: 'treecolumn', -
text: 'Name', -
flex: 1, -
sortable: true, -
dataIndex: 'name' -
} , { -
text: 'Population (millons)', -
sortable: true, -
width: 150, -
dataIndex: 'population' -
} , { -
text: 'Area (millons km^2)', -
width: 150, -
sortable: true, -
dataIndex: 'area' -
}] -
});
这是上面 Tree Grid 的输出结果:

Data views
Ext.view.View (xtype:dataview) 一个现实数据的自定义模板。你需要提供自定义的模板和数据源(store)。模板应该使用 Ext.XTemplate 。
data view 提供了内置的事件,例如 click,double-click,mouseover,mouseout,等等。
首先我们创建一个简单的 model 名为 Person ,还需要创建一个 store 并持有 Person 的列表,如以下代码所示:
-
Ext.define('Person', { -
extend : 'Ext.data.Model', -
fields : [ { -
name : 'name', -
type : 'string' -
}, { -
name : 'age', -
type : 'int' -
}, { -
name : 'gender', -
type : 'int' -
} ] -
}); -
Ext.create('Ext.data.Store', { -
id : 'employees', -
model : 'Person', -
data : [{ -
name : 'Mike', -
age : 22, -
gender : 0 -
},{ -
name : 'Woo', -
age : 32, -
gender : 1 -
},{ -
name : 'John', -
age : 33, -
gender : 1 -
},{ -
name : 'Kalai', -
age : 25, -
gender : 0 -
}] -
});
然后我们要来创建这个模板。下列模板使用 HTML 的 table 元素来呈现自定义格式的数据。
在模板中使用一个 model 的字段时,你可以使用花括号包括字段名的方式来使用它,例如:{fieldname}
XTemplate 支持有条件的展现和 if 语句,如以下代码所示:
-
var empTpl = new Ext.XTemplate( -
'<tpl for=".">', -
'<div style="margin-bottom: 10px;" class="data-view">', -
'<table style="width:100%">', -
'<tr>', -
'<td style="font-size: 100px;width:100px;" rowspan="3"><i class="fa fa-user"></i></td>', -
'<td>Name: {name}< /td>', -
'</tr>', -
'<tr>', -
'<td>Age:{age}< /td>', -
'</tr>', -
'<tr>', -
'<td>Gender: <tpl if="gender == 1">', -
'<i class="fa fa-mars"></i>', -
'<tpl else>', -
'<i class="fa fa-venus"></i>', -
'</tpl></td>', -
'</tr></table> ', -
'</div>', -
'</tpl>' -
);
看上面的例子,我使用了 awesome 字体图标的样式。你需要添加下列代码到你的 HTML 文件才行:
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font- awesome/4.3.0/css/font-awesome.min.css">
一下代码创建了一个 data view,并且它指定了使用的数据源 store ,template 和 itemSelector :
-
Ext.create('Ext.view.View', { -
store : Ext.getStore('employees'), -
tpl : empTpl, -
itemSelector : 'div.data-view', -
renderTo : Ext.getBody(), -
listeners : { -
itemclick : function(node, rec, item, index, e) { -
alert(rec.data.name); -
} -
} -
});
itemSelector 是一个必须的简单 CSS 选择器。这里 itemSelector 是应用于在 template 中的 HTML ,就是使用 data-view 类的 div 标签,最终根据这个模板,你在 data view 中选择的每一个 item ,就是这样一个 div 标签,设置了 itemSelector 属性,data view 会知道如何处理这些节点,itemSelector 是用于将 DOM 节点映射到 records 。
你可以监听的事件例如 click ,double-click ,等等,以上代码已经添加了监听,下列是输出结果:

图片浏览器 – 一个示例项目
惯例,我们将用一个示例项目来回顾本章所学,下面是示例项目的最终设计效果:

通过查看这个设计,你会看到我们使用的最重要的组件就是 tree panel 和 data view 。它们如何使用和一些概念已经在本章的前面部分提及。
我们看看, 项目的目录结构。

下列视图代码是本项目的重要部分。这个视图呈现了应用中大部分可视组件。它使用 tree panle 和 data view :
-
Ext.define('PE.view.pics.Pics', { -
extend : 'Ext.panel.Panel', -
/* Marks these are required classes to be to loaded before loading this view */ -
requires : [ 'PE.view.pics.PicsController' ], -
xtype : 'app-pics', -
controller : 'pics', -
items : [ { -
xtype : 'container', -
layout : 'hbox', -
cls : 'pics-list', -
items : [ { -
xtype : 'treepanel', -
width : 200, -
height : '100%', -
store : 'albums', -
border : true, -
useArrows : true, -
cls : 'tree', -
rootVisible : false, -
listeners : { -
itemdblclick : 'onNodeSelect' -
}, -
dockedItems : [ { -
xtype : 'toolbar', -
dock : 'top', -
ui : 'footer', -
items : [{ -
xtype : 'component', -
flex : 1 -
},{ -
xtype : 'button', -
text : 'Upload', -
cls : 'btn-blue' -
}] -
}] -
},{ -
xtype : 'dataview', -
reference : 'picsList', -
cls : 'pics-list-content', -
store : 'pics', -
tpl : [ -
'<tpl for=".">', -
'<div class="thumb"><img src="{url}" title=""></div>', -
'</tpl>' -
], -
multiSelect : true, -
minHeight : 400, -
flex : 1, -
trackOver : true, -
overItemCls : 'x-item-over', -
itemSelector : 'div.thumb', -
emptyText : 'No images to display' -
}] -
}] -
});
控制器 ViewController 里处理了 tree panel 的 itemdblclick 事件,只显示所选择节点下的图片。
还有一个 upload 按钮的 click 事件,这里是未处理的。额,这是你的作业啦。看看下列代码:
-
Ext.define('PE.view.pics.PicsController', { -
extend : 'Ext.app.ViewController', -
alias : 'controller.pics', -
views : [ 'PE.view.pics.Pics' ], -
requires : [ 'PE.store.Pics', 'PE.store.Albums' ], -
onNodeSelect : function(node, rec, item, index, e){ -
var albums = []; -
albums.push(rec.id); -
rec.childNodes.forEach(function(item) { -
albums.push(item.id); -
}); -
Ext.getStore('pics').filter({ -
property : 'albumId', -
operator : 'in', -
value : albums -
}); -
} -
});
Model 和 Store 的代码在这儿。
- 注意:当你不指定 model 的字段类型时,将会自动猜测类型。
-
Ext.define('Pic', { -
extend : 'Ext.data.Model', -
fields : [ 'id', 'url', 'albumId' ] -
}); -
Ext.define('PE.store.Pics', { -
extend : 'Ext.data.Store', -
storeId : 'pics', -
model : 'Pic', -
proxy : { -
type : 'rest', -
url : 'pics', // URL that will load data with respect to start and limit params -
reader : { -
type : 'json' -
} -
} -
}); -
Ext.create('PE.store.Pics').load(); -
Ext.define('PE.store.Albums', { -
extend : 'Ext.data.TreeStore', -
storeId : 'albums', -
root : { -
expanded : true, -
children : [ { -
id : 100, -
text : ' California', -
expanded : true, -
children : [ { -
id : 600, -
text : ' Big Sur', -
leaf : true -
}, { -
id : 500, -
text : ' Yosemite', -
leaf : true -
}] -
}, { -
id : 400, -
text : ' Arizona', -
expanded : true, -
children : [ { -
id : 300, -
text : ' Horseshoe bend', -
leaf : true -
}] -
}, { -
id : 200, -
text : ' Home', -
leaf : true -
}, { -
id : 700, -
text : ' India', -
expanded : true, -
children : [ { -
id : 800, -
text : ' Ooty', -
leaf : true -
}, { -
id : 900, -
text : ' Chennai', -
leaf : true -
}, { -
id : 1000, -
text : ' Munnar', -
leaf : true -
} ] -
} ] -
} -
}); -
Ext.create('PE.store.Albums');
我是用的 Go 语言为此项目写的 REST API 。完整可用的代码在这里 https://github.com/ananddayalan/extjs-byexample-picture-explorer
图片浏览器这个示例是一个非常简单并用来学习 tree panel 和 data view 使用是很合适的。也可以通过添加更多功能来改进这个例子。例如如何通过拖拽将图片从一个相册移动到另一个相册中。 我会留给你作为一个编码的练习,但在这里,我给你简要的概述一下拖拽功能,这将帮助你在此项目中添加拖拽功能。
拖拽
任意元素或组件都能支持拖拽。使用拖拽有三个重要的事情:
- 配置 item 为可拖拽的Configure the items as draggable
- 创建放置目标
- 完成放置目标
配置 item 为可拖拽的
想要拖拽一个 item ,你需要为每一个元素创建 Ext.dd.DD 实例。
查看下列代码,通过创建 Ext.dd.DD 让所有使用 pics 类的 div 元素成为可拖拽的:
-
// Configure the pics as draggable var pics = Ext.get('pics').select('div'); -
Ext.each(pics.elements, function(el) { -
var dd = Ext.create('Ext.dd.DD', el, ' picsDDGroup', { -
isTarget : false -
}); -
});
创建放置目标
使用 Ext.dd.DDTarget 创建放置容器。以下代码为所有的使用 album 类的 div 元素创建放置目标:
-
var albums = Ext.get('album').select('div'); -
Ext.each(albums.elements, function(el) { -
var albumDDTarget = Ext.create('Ext.dd.DDTarget', el, -
'picsDDGroup'); -
});
完成放置目标
当一个可拖拽项放置到一个放置容器,我们需要从这个 item 的源位置将它移动到目标位置。这通过覆盖 DD 的 onDragDrop 方法来实现。看一看下列代码:
-
var overrides = { -
onDragDrop : function(evtObj, targetElId) { -
var dropEl = Ext.get(targetElId); -
if (this.el.dom.parentNode.id != targetElId) { -
dropEl.appendChild(this.el); -
this.onDragOut(evtObj, targetElId); -
this.el.dom.style.position = ''; -
this.el.dom.style.top = ''; -
this.el.dom.style.left = ''; -
} else { -
this.onInvalidDrop(); -
} -
}, -
onInvalidDrop : function() { -
this.invalidDrop = true; -
} -
};
因为 DD 元素已经是实例了,重写的方法需要应用 Ext.apply(dd, overrides) ,如以下代码所示:
-
var albums = Ext.get('album').select('div'); -
var pics = Ext.get('pics').select('div'); -
Ext.each(pics.elements, function(el) { -
var dd = Ext.create('Ext.dd.DD', el, ' picsDDGroup', { -
isTarget : false -
}); -
Ext.apply(dd, overrides); -
});
总结
在本章中,你学习到如何使用拖拽功能。我们也看了几个高级组件:tree panel 和 data view。最后结合所学创建了一个示例项目。
本章围绕ExtJS高级组件展开,重点介绍了tree panel和data view组件。详细讲解了基本的tree、tree grid和Data views的使用方法,还给出了图片浏览器示例项目。此外,阐述了拖拽功能的实现步骤,包括配置可拖拽项、创建放置目标和完成放置目标。

被折叠的 条评论
为什么被折叠?



