[Ext JS 6 By Example 翻譯] 第6章 - 高階元件
轉載自:http://www.jeeboot.com/archives/1227.html
本章涵蓋了高階元件,比如 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。最後結合所學建立了一個示例專案。