溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

xmlplus組件設(shè)計(jì)系列之網(wǎng)格(DataGrid)(10)

發(fā)布時(shí)間:2020-10-09 10:29:01 來源:腳本之家 閱讀:159 作者:qudou 欄目:web開發(fā)

這一章我們要實(shí)現(xiàn)是一個(gè)網(wǎng)格組件,該組件除了最基本的數(shù)據(jù)展示功能外,還提供排序以及數(shù)據(jù)過濾功能。

xmlplus組件設(shè)計(jì)系列之網(wǎng)格(DataGrid)(10)

數(shù)據(jù)源

為了測(cè)試我們即將編寫好網(wǎng)格組件,我們采用如下格式的數(shù)據(jù)源。此數(shù)據(jù)源包含兩部分的內(nèi)容,分別是表頭數(shù)據(jù)集和表體數(shù)據(jù)集。網(wǎng)格組件實(shí)例最終的列數(shù)由表頭數(shù)據(jù)集的長(zhǎng)度決定。

var data = {
 gridColumns: ['name', 'power'],
 gridData: [
 { name: 'Chuck Norris', power: Infinity },
 { name: 'Bruce Lee', power: 9000 },
 { name: 'Jackie Chan', power: 7000 },
 { name: 'Jet Li', power: 8000 }
 ]
};

頂層設(shè)計(jì)

從視覺上,我們很自然地把網(wǎng)格組件劃分為表頭與表體。此網(wǎng)格組件有三個(gè)功能,所以應(yīng)該提供三個(gè)動(dòng)態(tài)接口。但我們注意到排序功能是通過點(diǎn)擊表頭進(jìn)行的,而表頭屬于網(wǎng)格組件的一部分,所以該功能應(yīng)該內(nèi)置。從而,實(shí)際上我們的網(wǎng)格組件對(duì)外只暴露兩個(gè)動(dòng)態(tài)接口:一個(gè)用于過濾,另一個(gè)用于接收數(shù)據(jù)源。所以我們可以得到如下的一個(gè)頂層設(shè)計(jì)。

DataGrid: {
 xml: `<table id='table'>
  <Thead id='thead'/>
  <Tbody id='tbody'/>
  </table>`,
 fun: function (sys, items, opts) {
 function setValue(data) {
  items.thead.val(data.gridColumns);
  items.tbody.val(data.gridColumns, data.gridData);
 }
 function filter(filterKey) {
  // 過濾函數(shù)
 }
 return { val: setValue, filter: filter };
 }
}

設(shè)計(jì)表頭

表頭只有一行,所以可以直接給它提供一個(gè) tr 元素。tr 元素的子級(jí)項(xiàng) th 的個(gè)數(shù)取決于表頭數(shù)據(jù)集的長(zhǎng)度,所以需要?jiǎng)討B(tài)創(chuàng)建。由于 th 元素包含了排序功能,所以需要另行封裝。下面是我們給出的表頭的設(shè)計(jì)。

Thead: {
 xml: `<thead id='thead'>
  <tr id='tr'/>
  </thead>`,
 fun: function (sys, items, opts) {
 function setValue(value) {
  sys.tr.children().call("remove");
  data.forEach(item => sys.tr.append("Th").value().val(item));
 }
 return { val: setValue };
 }
}

表頭數(shù)據(jù)項(xiàng)組件提供一個(gè)文本設(shè)置接口。該組件本身并不負(fù)責(zé)排序,它只完成自身視圖狀態(tài)的變更以及排序命令的派發(fā)。排序命令的派發(fā)需要攜帶兩個(gè)數(shù)據(jù):一個(gè)是排序關(guān)鍵字,也就是表頭文本;另一個(gè)排序方向,升或者降。

Th: {
 css: "#active { color: #fff; } #active #arrow { opacity: 1; } #active #key { color: #fff; }\
  #arrow { display: inline-block; vertical-align: middle; width: 0; height: 0; margin-left: 5px; opacity: 0.66; }\
  #asc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-bottom: 4px solid #fff;}\
  #dsc { border-left: 4px solid transparent; border-right: 4px solid transparent; border-top: 4px solid #fff; }",
 xml: "<th id='th'>\
  <span id='key'/><span id='arrow'/>\
  </th>",
 fun: function (sys, items, opts) {
 var order = "#asc";
 this.watch("sort", function (e, key, order) {
  sys.key.text().toLowerCase() == key || sys.th.removeClass("#active");
 });
 this.on("click", function (e) {
  sys.th.addClass("#active");
  sys.arrow.removeClass(order);
  order = order == "#asc" ? "#dsc" : "#asc";
  sys.arrow.addClass(order).notify("sort", [sys.key.text().toLowerCase(), order]);
 });
 sys.arrow.addClass("#asc");
 return { val: sys.key.text };
 }
}

設(shè)計(jì)表體

表體可以有多行,但表體只負(fù)責(zé)展示數(shù)據(jù),所以實(shí)現(xiàn)起來比表頭要簡(jiǎn)單的多。

Tbody: {
 xml: `<tbody id='tbody'/>`,
 fun: function (sys, items, opts) {
 function setValue(gridColumns, gridData) {
  sys.tbody.children().call("remove");
  gridData.forEach(data => 
  tr = sys.tbody.append("tr");
  gridColumns.forEach(key => tr.append("td").text(data[key]));
  ));
 }
 return { val: setValue };
 }
}

加入排序功能

為了便于管理,我們把排序功能單獨(dú)封裝成一個(gè)組件,該組件提供一個(gè)排序接口,同時(shí)偵聽一個(gè)排序消息。一旦接收到排序消息,則記錄下關(guān)鍵字與排序方向,并派發(fā)一個(gè)表體刷新命令。

Sort: {
 fun: function (sys, items, opts) {
 var sortKey, sortOrder;
 this.watch("sort", function (e, key, order) {
  sortKey = key, sortOrder = order;
  this.trigger("update");
 });
 return function (data) {
  return sortKey ? data.slice().sort(function (a, b) {
  a = a[sortKey], b = b[sortKey];
  return (a === b ? 0 : a > b ? 1 : -1) * (sortOrder == "#asc" ? 1 : -1);
  }) : data;
 };
 }
}

要完整地實(shí)現(xiàn)排序功能,對(duì)組件 DataGrid 作一些修正,主要是內(nèi)置上述的排序功能組件并偵聽表體刷新指令。一旦接收到刷新指令,則對(duì)表體數(shù)據(jù)完成排序并刷新表體。

DataGrid: {
 xml: `<table id='table'>
  <Thead id='thead'/>
  <Tbody id='tbody'/>
  <Sort id='sort'/>
  </table>`,
 fun: function (sys, items, opts) {
 var data = {gridColumns: [], gridData: []};
 function setValue(value) {
  data = value;
  items.thead.val(data.gridColumns);
  items.tbody.val(data.gridColumns, data.gridData);
 }
 function filter(filterKey) {
  // 過濾函數(shù)
 }
 this.on("update", function() {
  items.tbody.val(items.sort(data.gridData));
 });
 return { val: setValue, filter: filter };
 }
}

加入過濾功能

與排序功能的加入流程類似,我們把過濾功能單獨(dú)封裝成一個(gè)組件,該組件提供一個(gè)過濾接口,同時(shí)偵聽一個(gè)過濾消息。一旦接收到消息,則記錄下過濾關(guān)鍵字,并派發(fā)一個(gè)表體刷新命令。

Filter: {
 fun: function (sys, items, opts) {
 var filterKey = "";
 this.watch("filter", function (e, key) {
  filterKey = key.toLowerCase();
  this.trigger("update");
 });
 return function (data) {
  return data.filter(function (row) {
  return Object.keys(row).some(function (key) {
   return String(row[key]).toLowerCase().indexOf(filterKey) > -1;
  });
  });
 };
 }
}

另外需要對(duì)組件 DataGrid 作一些修正,修正內(nèi)容與上述的排序功能的加入類似,區(qū)別在于額外完善了 filter 接口以及對(duì)消息作用域進(jìn)行了限制。下面是我們最終的網(wǎng)格組件。

DataGrid: {
 css: `#table { border: 2px solid #42b983; border-radius: 3px; background-color: #fff; }
  #table th { background-color: #42b983; color: rgba(255,255,255,0.66); cursor: pointer; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; }
  #table td { background-color: #f9f9f9; }
  #table th, #table td { min-width: 120px; padding: 10px 20px; }`,
 xml: `<table id='table'>
  <Thead id='thead'/>
  <Tbody id='tbody'/>
  <Sort id='sort'/>
  <Filter id='filter'/>
  </table>`,
 map: { msgscope: true },
 fun: function (sys, items, opts) {
 var data = {gridColumns: [], gridData: []};
 function setValue(value) {
  data = value;
  items.thead.val(data.gridColumns);
  items.tbody.val(data.gridColumns, data.gridData);
 }
 function filter(filterKey) {
  sys.table.notify("filter", filterKey);
 }
 this.on("update", function() {
  items.tbody.val(items.filter(items.sort(data.gridData)));
 });
 return { val: setValue, filter: filter };
 }
}

值得注意的是這里一定要在映射項(xiàng)中配置限制消息作用域的選項(xiàng)。否則,當(dāng)在一個(gè)應(yīng)用中實(shí)例化多個(gè)網(wǎng)格組件時(shí),消息就會(huì)互相干擾。

測(cè)試

最后我們來測(cè)試下我們完成的組件,測(cè)試的功能主要就是剛開始提到的三個(gè):數(shù)據(jù)展示、排序以及過濾。

Index: {
 css: "#index { font-family: Helvetica Neue, Arial, sans-serif; font-size: 14px; color: #444; }\
  #search { margin: 8px 0; }",
 xml: "<div id='index'>\
  Search <input id='search'/>\
  <Table id='table'/>\
  </div>",
 fun: function (sys, items, opts) {
 items.table.val(data);
 sys.search.on("input", e => items.table.filter(sys.search.prop("value")));
 }
}

本系列文章基于 xmlplus 框架。如果你對(duì) xmlplus 沒有多少了解,可以訪問 www.xmlplus.cn。這里有詳盡的入門文檔可供參考。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI