xmlplus is a JavaScriptframework used for rapid development of front-end and back-end projects. This article mainly introduces the xmlplus grid in the xmlplus component design series. It has a certain reference value. Interested friends can refer to
. In this chapter, we are going to implement a grid component. This component In addition to the most basic data display function, it also provides sorting and data filtering functions.
Data source
In order to test that we are about to write the grid component, we use the data source in the following format. This data source contains two parts, namely header data set and table body data set. The final number of columns in the grid component instance is determined by the length of the header data set.
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 } ] };
Top-level design
Visually, we naturally divide the grid component into a table header and a table body. This grid component has three functions, so it should provide three dynamic interfaces. But we noticed that the sorting function is performed by clicking on the table header, and the table header is part of the grid component, so this function should be built-in. Therefore, in fact, our grid component only exposes two dynamic interfaces to the outside world: one for filtering and the other for receiving data sources. So we can get a top-level design as follows.
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) { // 过滤函数 } return { val: setValue, filter: filter }; } }
Design header
The header has only one row, so you can directly provide it with a tr element. The number of child items th of the tr element depends on the length of the header data set, so it needs to be created dynamically. Since the th element contains the sorting function, it needs to be encapsulated separately. Below is the design of the header we gave.
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 }; } }
The header data item component provides a text setting interface. The component itself is not responsible for sorting. It only completes the change of its viewstate and the dispatch of sorting commands. The dispatch of sorting commands needs to carry two pieces of data: one is the sorting keyword, which is the header text; the other is the sorting direction, ascending or descending.
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 }; } }
Design table body
The table body can have multiple rows, but the table body is only responsible for displaying data, so it is much simpler to implement than the table header. .
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 }; } }
Add sorting function
In order to facilitate management, we separately encapsulate the sorting function into a component, which provides a sorting interface and listens to a Sort messages. Once the sorting message is received, the keywords and sorting direction are recorded, and a table refresh command is issued.
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; }; } }
To fully realize the sorting function, make some modifications to the component DataGrid, mainly to build in the above sorting function component and listen for table body refresh instructions. Once the refresh command is received, the table body data is sorted and the table body is refreshed.
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) { // 过滤函数 } this.on("update", function() { items.tbody.val(items.sort(data.gridData)); }); return { val: setValue, filter: filter }; } }
Add filtering function
Similar to the process of adding sorting function, we encapsulate the filtering function into a separate component, which provides a filtering interface. Also listen for a filtered message. Once the message is received, the filter keyword is recorded and a table body refresh command is dispatched.
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; }); }); }; } }
In addition, some modifications need to be made to the component DataGrid. The modification content is similar to the addition of the above-mentioned sorting function. The difference is that the filter interface is additionally improved and the message scope is restricted. Below is our final grid component.
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 }; } }
It is worth noting that the option to limit the scope of the message must be configured in the mapping item. Otherwise, when multiple grid components are instantiated in an application, messages will interfere with each other.
Testing
Finally, let’s test the components we completed. The main functions of the test are the three mentioned at the beginning: data display, sorting and filter.
Index: { css: "#index { font-family: Helvetica Neue, Arial, sans-serif; font-size: 14px; color: #444; }\ #search { margin: 8px 0; }", xml: "<p id='index'>\ Search <input id='search'/>\ <Table id='table'/>\ </p>", fun: function (sys, items, opts) { items.table.val(data); sys.search.on("input", e => items.table.filter(sys.search.prop("value"))); } }
This series of articles is based on the xmlplus framework. If you don’t know much about xmlplus, you can visit www.xmlplus.cn. Detailed getting started documentation is available here.
【Related recommendations】
1. Free js online video tutorial
2. JavaScript Chinese Reference Manual
3. php.cn Dugu Jiujian (3) - JavaScript video tutorial
The above is the detailed content of Introduction to JavaScript framework (xmlplus) components (10) Grid (DataGrid). For more information, please follow other related articles on the PHP Chinese website!