1. 程式人生 > >[Ext JS 6 By Example 翻譯] 第6章 - 高階元件

[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 並硬編碼內建資料:

 

 
  1. var store = Ext.create('Ext.data.TreeStore', {

  2. root: {

  3. expanded: true,

  4. text: 'Continents',

  5. children: [{

  6. text: 'Antarctica',

  7. leaf: true

  8. }, {

  9. text: 'South America',

  10. expanded: true,

  11. children: [{

  12. text: 'Brazil',

  13. leaf: true

  14. }, {

  15. text: 'Chile',

  16. leaf: true

  17. }]

  18. }, {

  19. text: 'Asia',

  20. expanded: true,

  21. children: [{

  22. text: 'India',

  23. leaf: true

  24. },{

  25. text: 'China',

  26. leaf: true

  27. }]

  28. }, {

  29. text: 'Africa',

  30. leaf: true

  31. }]

  32. }

  33. });

 

接著繼續建立 Ext.tree.Panel :

 

 
  1. Ext.create('Ext.tree.Panel', {

  2. title: 'Basic Tree',

  3. width: 200,

  4. height: 450,

  5. store: store,

  6. rootVisible: true,

  7. renderTo: Ext.getBody()

  8. });


 

 

下列截圖為以上程式碼的輸出結果:

現在,我們建立一個高階點的樹,它是可以拖拽的。同時還需要用到 tree panel 和 tree store 的一些額外選項。拖拽只需要新增一個外掛叫做 treeviewdragdrop 。如以下程式碼所示:

 

 
  1. var store = Ext.create('Ext.data.TreeStore', {

  2. root: {

  3. expanded: true,

  4. text: 'Continents',

  5. checked: false,

  6. children: [{

  7. text: 'Antarctica',

  8. leaf: true ,

  9. checked: false

  10. },{

  11. text: 'South America',

  12. expanded: false,

  13. checked: true,

  14. children: [{

  15. text: 'Chile',

  16. leaf: true,

  17. checked: true

  18. }]

  19. },{

  20. text: 'Asia',

  21. expanded: true,

  22. checked: true,

  23. children: [{

  24. text: 'India',

  25. leaf: true,

  26. checked: true

  27. },{

  28. text: 'China',

  29. leaf: true,

  30. checked: true

  31. }]

  32. },{

  33. text: 'Africa',

  34. leaf: true,

  35. checked: true

  36. }]

  37. }

  38. });

  39.  
  40. Ext.create('Ext.tree.Panel', {

  41. title: 'Basic Tree',

  42. width: 200,

  43. height: 450,

  44. store: store,

  45. rootVisible: true,

  46. useArrows: true,

  47. lines: false,

  48. renderTo: Ext.getBody(),

  49. viewConfig: {

  50. plugins: {

  51. ptype: 'treeviewdragdrop',

  52. containerScroll: true

  53. }

  54. }

  55. });

 

 

如以下截圖所示的輸出。我把節點  South America 拖拽至  Asia 節點之下:

tree grid

你可以將多個列新增到 tree ,同時也能建立 tree grid 。預設 tree 包含一列,用的是 tree store 中節點的文字欄位。

在這個 store 中,你可以看到在每個節點上除了節點名稱,還添加了一些其他的欄位,這些欄位用於在 tree panel 的列展示上。tree grid 的功能有例如 列調整,排序,過濾等等,以下是程式碼:

 

 
  1. var store = Ext.create('Ext.data.TreeStore', {

  2. root: {

  3. expanded: true,

  4. text: 'Continents',

  5. children: [{

  6. name: 'Antarctica',

  7. population: 0,

  8. area: 14,

  9. leaf: true

  10. },{

  11. name: 'South America',

  12. population: 385 ,

  13. area: 17.84,

  14. expanded: false,

  15. children: [{

  16. name: 'Chile',

  17. population: 18,

  18. area: 0.7,

  19. leaf: true,

  20. }]

  21. },{

  22. name: 'Asia',

  23. expanded: true,

  24. population: 4164,

  25. area: 44.57,

  26. children: [{

  27. name: 'India',

  28. leaf: true,

  29. population: 1210,

  30. area: 3.2

  31. },{

  32. name: 'China',

  33. leaf: true,

  34. population: 1357,

  35. area: 9.5

  36. }]

  37. },{

  38. name: 'Africa',

  39. leaf: true,

  40. population: 1110,

  41. area: 30

  42. }]

  43. }

  44. });

 

 

以下的 grid 和上面的 tree panel 差不多一樣,只是新增為多列了,這個 xtyp treecolumn 提供縮排和資料夾結構。像一個正常的 grid 一樣,tree grid 的列可以是任意型別的例如 checkbox,picture,button,URL 等等。

預設列大小是可調整的,如果需要你也可以固定它的寬度。看下面的程式碼:

 

 
  1. Ext.create('Ext.tree.Panel', {

  2. title: 'Tree Grid',

  3. width: 500,

  4. height: 450,

  5. store: store,

  6. rootVisible: false,

  7. useArrows: true,

  8. lines: false,

  9. scope: this,

  10. renderTo: Ext.getBody(),

  11. columns: [{

  12. xtype: 'treecolumn',

  13. text: 'Name',

  14. flex: 1,

  15. sortable: true,

  16. dataIndex: 'name'

  17. } , {

  18. text: 'Population (millons)',

  19. sortable: true,

  20. width: 150,

  21. dataIndex: 'population'

  22. } , {

  23. text: 'Area (millons km^2)',

  24. width: 150,

  25. sortable: true,

  26. dataIndex: 'area'

  27. }]

  28. });

 

這是上面 Tree Grid 的輸出結果:

 

Data views

Ext.view.View (xtype:dataview) 一個現實資料的自定義模板。你需要提供自定義的模板和資料來源(store)。模板應該使用 Ext.XTemplate 。

data view 提供了內建的事件,例如 click,double-click,mouseover,mouseout,等等。

首先我們建立一個簡單的 model 名為 Person ,還需要建立一個 store 並持有 Person 的列表,如以下程式碼所示:

 

 
  1. Ext.define('Person', {

  2. extend : 'Ext.data.Model',

  3. fields : [ {

  4. name : 'name',

  5. type : 'string'

  6. }, {

  7. name : 'age',

  8. type : 'int'

  9. }, {

  10. name : 'gender',

  11. type : 'int'

  12. } ]

  13. });

  14.  
  15. Ext.create('Ext.data.Store', {

  16. id : 'employees',

  17. model : 'Person',

  18. data : [{

  19. name : 'Mike',

  20. age : 22,

  21. gender : 0

  22. },{

  23. name : 'Woo',

  24. age : 32,

  25. gender : 1

  26. },{

  27. name : 'John',

  28. age : 33,

  29. gender : 1

  30. },{

  31. name : 'Kalai',

  32. age : 25,

  33. gender : 0

  34. }]

  35. });

 

 

然後我們要來建立這個模板。下列模板使用 HTML 的 table 元素來呈現自定義格式的資料。

在模板中使用一個 model 的欄位時,你可以使用花括號包括欄位名的方式來使用它,例如:{fieldname}

XTemplate 支援有條件的展現和 if 語句,如以下程式碼所示:

 

 
  1. var empTpl = new Ext.XTemplate(

  2. '<tpl for=".">',

  3. '<div style="margin-bottom: 10px;" class="data-view">',

  4. '<table style="width:100%">',

  5. '<tr>',

  6. '<td style="font-size: 100px;width:100px;" rowspan="3"><i class="fa fa-user"></i></td>',

  7. '<td>Name: {name}< /td>',

  8. '</tr>',

  9.  
  10. '<tr>',

  11. '<td>Age:{age}< /td>',

  12. '</tr>',

  13.  
  14. '<tr>',

  15. '<td>Gender: <tpl if="gender == 1">',

  16. '<i class="fa fa-mars"></i>',

  17. '<tpl else>',

  18. '<i class="fa fa-venus"></i>',

  19. '</tpl></td>',

  20. '</tr></table> ',

  21. '</div>',

  22. '</tpl>'

  23. );

 

看上面的例子,我使用了 awesome 字型圖示的樣式。你需要新增下列程式碼到你的 HTML 檔案才行:

 

<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font- awesome/4.3.0/css/font-awesome.min.css">

 

 

一下程式碼建立了一個 data view,並且它指定了使用的資料來源 store ,template 和 itemSelector :

 

 
  1. Ext.create('Ext.view.View', {

  2. store : Ext.getStore('employees'),

  3. tpl : empTpl,

  4. itemSelector : 'div.data-view',

  5. renderTo : Ext.getBody(),

  6. listeners : {

  7. itemclick : function(node, rec, item, index, e) {

  8. alert(rec.data.name);

  9. }

  10. }

  11. });

 

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 :

 

 
  1. Ext.define('PE.view.pics.Pics', {

  2. extend : 'Ext.panel.Panel',

  3. /* Marks these are required classes to be to loaded before loading this view */

  4. requires : [ 'PE.view.pics.PicsController' ],

  5. xtype : 'app-pics',

  6. controller : 'pics',

  7. items : [ {

  8. xtype : 'container',

  9. layout : 'hbox',

  10. cls : 'pics-list',

  11. items : [ {

  12. xtype : 'treepanel',

  13. width : 200,

  14. height : '100%',

  15. store : 'albums',

  16. border : true,

  17. useArrows : true,

  18. cls : 'tree',

  19. rootVisible : false,

  20. listeners : {

  21. itemdblclick : 'onNodeSelect'

  22. },

  23. dockedItems : [ {

  24. xtype : 'toolbar',

  25. dock : 'top',

  26. ui : 'footer',

  27. items : [{

  28. xtype : 'component',

  29. flex : 1

  30. },{

  31. xtype : 'button',

  32. text : 'Upload',

  33. cls : 'btn-blue'

  34. }]

  35. }]

  36. },{

  37. xtype : 'dataview',

  38. reference : 'picsList',

  39. cls : 'pics-list-content',

  40. store : 'pics',

  41. tpl : [

  42. '<tpl for=".">',

  43. '<div class="thumb"><img src="{url}" title=""></div>',

  44. '</tpl>'

  45. ],

  46. multiSelect : true,

  47. minHeight : 400,

  48. flex : 1,

  49. trackOver : true,

  50. overItemCls : 'x-item-over',

  51. itemSelector : 'div.thumb',

  52. emptyText : 'No images to display'

  53. }]

  54. }]

  55. });

 

控制器 ViewController 裡處理了 tree panel 的 itemdblclick 事件,只顯示所選擇節點下的圖片。

還有一個 upload 按鈕的 click 事件,這裡是未處理的。額,這是你的作業啦。看看下列程式碼:

 

 
  1. Ext.define('PE.view.pics.PicsController', {

  2. extend : 'Ext.app.ViewController',

  3. alias : 'controller.pics',

  4. views : [ 'PE.view.pics.Pics' ],

  5. requires : [ 'PE.store.Pics', 'PE.store.Albums' ],

  6. onNodeSelect : function(node, rec, item, index, e){

  7. var albums = [];

  8. albums.push(rec.id);

  9. rec.childNodes.forEach(function(item) {

  10. albums.push(item.id);

  11. });

  12.  
  13. Ext.getStore('pics').filter({

  14. property : 'albumId',

  15. operator : 'in',

  16. value : albums

  17. });

  18. }

  19. });

 

 

Model 和 Store 的程式碼在這兒。

  • 注意:當你不指定 model 的欄位型別時,將會自動猜測型別。

 

 
  1. Ext.define('Pic', {

  2. extend : 'Ext.data.Model',

  3. fields : [ 'id', 'url', 'albumId' ]

  4. });

  5.  
  6. Ext.define('PE.store.Pics', {

  7. extend : 'Ext.data.Store',

  8. storeId : 'pics',

  9. model : 'Pic',

  10. proxy : {

  11. type : 'rest',

  12. url : 'pics', // URL that will load data with respect to start and limit params

  13. reader : {

  14. type : 'json'

  15. }

  16. }

  17. });

  18.  
  19. Ext.create('PE.store.Pics').load();

  20.  
  21. Ext.define('PE.store.Albums', {

  22. extend : 'Ext.data.TreeStore',

  23. storeId : 'albums',

  24. root : {

  25. expanded : true,

  26. children : [ {

  27. id : 100,

  28. text : ' California',

  29. expanded : true,

  30. children : [ {

  31. id : 600,

  32. text : ' Big Sur',

  33. leaf : true

  34. }, {

  35. id : 500,

  36. text : ' Yosemite',

  37. leaf : true

  38. }]

  39. }, {

  40. id : 400,

  41. text : ' Arizona',

  42. expanded : true,

  43. children : [ {

  44. id : 300,

  45. text : ' Horseshoe bend',

  46. leaf : true

  47. }]

  48. }, {

  49. id : 200,

  50. text : ' Home',

  51. leaf : true

  52. }, {

  53. id : 700,

  54. text : ' India',

  55. expanded : true,

  56. children : [ {

  57. id : 800,

  58. text : ' Ooty',

  59. leaf : true

  60. }, {

  61. id : 900,

  62. text : ' Chennai',

  63. leaf : true

  64. }, {

  65. id : 1000,

  66. text : ' Munnar',

  67. leaf : true

  68. } ]

  69. } ]

  70. }

  71. });

  72.  
  73. 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 元素成為可拖拽的:

 

 
  1. // Configure the pics as draggable var pics = Ext.get('pics').select('div');

  2. Ext.each(pics.elements, function(el) {

  3. var dd = Ext.create('Ext.dd.DD', el, ' picsDDGroup', {

  4. isTarget : false

  5. });

  6. });

 

 

建立放置目標

使用 Ext.dd.DDTarget 建立放置容器。以下程式碼為所有的使用 album 類的 div 元素建立放置目標:

 

 
  1. var albums = Ext.get('album').select('div');

  2. Ext.each(albums.elements, function(el) {

  3. var albumDDTarget = Ext.create('Ext.dd.DDTarget', el,

  4. 'picsDDGroup');

  5. });

 

 

完成放置目標

當一個可拖拽項放置到一個放置容器,我們需要從這個 item 的源位置將它移動到目標位置。這通過覆蓋 DD 的 onDragDrop 方法來實現。看一看下列程式碼:

 

 
  1. var overrides = {

  2. onDragDrop : function(evtObj, targetElId) {

  3. var dropEl = Ext.get(targetElId);

  4. if (this.el.dom.parentNode.id != targetElId) {

  5. dropEl.appendChild(this.el);

  6. this.onDragOut(evtObj, targetElId);

  7. this.el.dom.style.position = '';

  8. this.el.dom.style.top = '';

  9. this.el.dom.style.left = '';

  10. } else {

  11. this.onInvalidDrop();

  12. }

  13. },

  14. onInvalidDrop : function() {

  15. this.invalidDrop = true;

  16. }

  17. };

 

 

因為 DD 元素已經是例項了,重寫的方法需要應用 Ext.apply(dd, overrides) ,如以下程式碼所示:

 

 
  1. var albums = Ext.get('album').select('div');

  2. var pics = Ext.get('pics').select('div');

  3. Ext.each(pics.elements, function(el) {

  4. var dd = Ext.create('Ext.dd.DD', el, ' picsDDGroup', {

  5. isTarget : false

  6. });

  7. Ext.apply(dd, overrides);

  8. });


 

 

總結

在本章中,你學習到如何使用拖拽功能。我們也看了幾個高階元件:tree panel 和 data view。最後結合所學建立了一個示例專案。