我们知道,在FlexGrid中默认双击单元格时,只有普通的输入框可供用户键盘输入内容,
能否把功能丰富的Input组件引入到FlexGrid中呢?答案是肯定的,这个示例告诉我们没什么不可能:
https://demo.grapecity.com.cn/wijmo/demos/Grid/CustomCells/CustomEditors/purejs
自定义编辑器,实际上就是利用了FlexGrid的beginningEdit等事件,替换掉原生的input元素,
引入自定义的元素来实现原生input所不具备的功能。示例中演示了数字、下拉、日期三种input组件,
但实际应用中,我们的数据类型可能是多种多样的,比如今天讲到的,DropdownTree组件,如图:
想在FlexGrid中加入这个组件,面临的第一个问题是,WijmoJS本身并没有这个组件的实现,不过没关系,
关于这个问题,我直接给出解决方案,大家可以直接下载附件的“DropDownTree.js”,这个js文件扩展了wijmo的功能,
实现了这个功能,大家可以直接引用到项目中即可,如下:
- new wijmo.input.DropDownTree()
复制代码
好了,有了这个组件,第二步就是把它集成到FlexGrid中。我们利用“自定义编辑器”这个示例的基本结构,
通过一些修改,来实现集成DropDownTree的功能,如下:
- <!DOCTYPE html>
- <html>
- <head>
- <meta charset="UTF-8">
- <title></title>
- <link href="css/wijmo.min.css" rel="stylesheet" />
- <script src="js/wijmo.min.js"></script>
- <script src="js/wijmo.nav.min.js"></script>
- <script src="js/wijmo.grid.min.js"></script>
- <script src="js/wijmo.grid.filter.min.js"></script>
- <script src="js/wijmo.input.min.js"></script>
- <script src="js/wijmo.culture.zh.min.js"></script>
- <script src="js/DropDownTree.js"></script>
- <style>
- body {
- margin-bottom: 24px;
- }
- .wj-flexgrid {
- max-height: 300px;
- }
- </style>
- </head>
- <body>
- <div class="container-fluid">
- <div id="theGrid">
- </div>
- </div>
- <script>
- class CustomGridEditor {
- /**
- * Initializes a new instance of a CustomGridEditor.
- */
- constructor(flex,binding, edtClass, options) {
- // console.log(binding, edtClass, options)
- // save references
- this._grid = flex;
- // this._col = flex.columns.getColumn(binding);
- this._col = flex.rows[binding];
-
- this._binding = flex.columns[binding];
-
- console.log( flex.rows )
- console.log( this._col )
- // create editor
- this._ctl = new edtClass(document.createElement('div'), options);
- // connect grid events
- flex.beginningEdit.addHandler(this._beginningEdit, this);
- flex.sortingColumn.addHandler(() => {
- this._commitRowEdits();
- });
- flex.scrollPositionChanged.addHandler(() => {
- if (this._ctl.containsFocus()) {
- flex.focus();
- }
- });
- flex.selectionChanging.addHandler((s, e) => {
- if (e.row != s.selection.row) {
- this._commitRowEdits();
- }
- });
- // connect editor events
- this._ctl.addEventListener(this._ctl.hostElement, 'keydown', (e) => {
- switch (e.keyCode) {
- case wijmo.Key.Tab:
- case wijmo.Key.Enter:
- e.preventDefault(); // TFS 255685
- this._closeEditor(true);
- this._grid.focus();
- // forward event to the grid so it will move the selection
- var evt = document.createEvent('HTMLEvents');
- evt.initEvent('keydown', true, true);
- 'altKey,metaKey,ctrlKey,shiftKey,keyCode'.split(',').forEach((prop) => {
- evt[prop] = e[prop];
- });
- this._grid.hostElement.dispatchEvent(evt);
- break;
- case wijmo.Key.Escape:
- this._closeEditor(false);
- this._grid.focus();
- break;
- }
- });
- // close the editor when it loses focus
- this._ctl.lostFocus.addHandler(() => {
- setTimeout(() => {
- if (!this._ctl.containsFocus()) {
- this._closeEditor(true); // apply edits and close editor
- this._grid.onLostFocus(); // commit item edits if the grid lost focus
- }
- });
- });
- // commit edits when grid loses focus
- this._grid.lostFocus.addHandler(() => {
- setTimeout(() => {
- if (!this._grid.containsFocus() && !CustomGridEditor._isEditing) {
- this._commitRowEdits();
- }
- });
- });
- // open drop-down on f4/alt-down
- this._grid.addEventListener(this._grid.hostElement, 'keydown', (e) => {
- // open drop-down on f4/alt-down
- this._openDropDown = false;
- if (e.keyCode == wijmo.Key.F4 ||
- (e.altKey && (e.keyCode == wijmo.Key.Down || e.keyCode == wijmo.Key.Up))) {
- var colIndex = this._grid.selection.col;
- console.log( this._grid.selection.col)
- if (colIndex > -1 && this._grid.rows[colIndex] == this._col) {
- this._openDropDown = true;
- this._grid.startEditing(true);
- e.preventDefault();
- }
- }
- // commit edits on Enter (in case we're at the last row, TFS 268944)
- if (e.keyCode == wijmo.Key.Enter) {
- this._commitRowEdits();
- }
- }, true);
- // close editor when user resizes the window
- // REVIEW: hides editor when soft keyboard pops up (TFS 326875)
- window.addEventListener('resize', () => {
- if (this._ctl.containsFocus()) {
- this._closeEditor(true);
- this._grid.focus();
- }
- });
- }
- // gets an instance of the control being hosted by this grid editor
- get control() {
- return this._ctl;
- };
-
- // 遍历查找树节点
- _findNode(treeNode, key, val, nodeArr){
- if(treeNode.dataItem && treeNode.dataItem[key] == val){
- nodeArr.push(treeNode);
- }else if(treeNode.nodes && treeNode.nodes.length>0){
- for(let i=0; i<treeNode.nodes.length; i++){
- if(treeNode.nodes[i].dataItem[key] == val){
- nodeArr.push(treeNode.nodes[i]);
- }else{
- this._findNode(treeNode.nodes[i], key, val, nodeArr);
- }
- }
- }
- };
-
- // handle the grid's beginningEdit event by canceling the built-in editor,
- // initializing the custom editor and giving it the focus.
- _beginningEdit(grid, args) {
- // check that this is our column
- // console.log(args)
- if (grid.columns[args.col] != this._binding) {
- // console.log(grid.columns[args.col])/
- return;
- }
- // check that this is not the Delete key
- // (which is used to clear cells and should not be messed with)
- var evt = args.data;
- if (evt && evt.keyCode == wijmo.Key.Delete) {
- return;
- }
- // cancel built-in editor
- args.cancel = true;
- // save cell being edited
- this._rng = args.range;
- CustomGridEditor._isEditing = true;
- // initialize editor host
- var rcCell = grid.getCellBoundingRect(args.row, args.col), rcBody = document.body.getBoundingClientRect(), ptOffset = new wijmo.Point(-rcBody.left, -rcBody.top), zIndex = (args.row < grid.frozenRows || args.col < grid.frozenColumns) ? '3' : '';
- wijmo.setCss(this._ctl.hostElement, {
- position: 'absolute',
- left: rcCell.left - 1 + ptOffset.x+8,
- top: rcCell.top - 1 + ptOffset.y+8,
- width: rcCell.width + 1,
- height: grid.rows[args.row].renderHeight + 1,
- borderRadius: '0px',
- zIndex: zIndex,
- });
- // initialize editor content
- /**
- 这句代码是负责在展开下拉前更新下拉状态的
- 在此处清空勾选状态即可。
- */
- if (!wijmo.isUndefined(this._ctl['checkedItems']) && this._ctl['checkedItems'].length > 0) {
- this._ctl['checkedItems'] = [];
- }
-
-
- // 这里设置回填数据
- var cellVal = grid.getCellData(args.row, args.col);
- var valArr = null;
- if(cellVal && cellVal.length > 0){
- valArr = cellVal.split(",");
- }
- if(valArr){
- var treeData = this._treeData;
- var findNode = this._findNode;
- var _this = this;
- var checkedNodes = [];
- for(let i=0; i<valArr.length; i++){
- this._ctl.nodes.forEach(function(node){
- findNode.call(_this, node, "header", valArr[i], checkedNodes);
- });
- }
- checkedNodes.forEach(function(node){
- node.isChecked = true;
- });
- }
-
- /*
- else {
- throw 'Can\'t set editor value/text...';
- }
- */
- // start editing item
- var ecv = grid.editableCollectionView, item = grid.rows[args.row].dataItem;
- if (ecv && item && item != ecv.currentEditItem) {
- setTimeout(function () {
- grid.onRowEditStarting(args);
- ecv.editItem(item);
- grid.onRowEditStarted(args);
- }, 50); // wait for the grid to commit edits after losing focus
- }
- // activate editor
- document.body.appendChild(this._ctl.hostElement);
- this._ctl.focus();
- setTimeout(() => {
-
- // get the key that triggered the editor
- var key = (evt && evt.charCode > 32)
- ? String.fromCharCode(evt.charCode)
- : null;
- // get input element in the control
- var input = this._ctl.hostElement.querySelector('input');
- // send key to editor
- if (input) {
- if (key) {
- input.value = key;
- wijmo.setSelectionRange(input, key.length, key.length);
- var evtInput = document.createEvent('HTMLEvents');
- evtInput.initEvent('input', true, false);
- input.dispatchEvent(evtInput);
- }
- else {
- input.select();
- }
- }
- // give the control focus
- if (!input && !this._openDropDown) {
- this._ctl.focus();
- }
- // open drop-down on F4/alt-down
- if (this._openDropDown && this._ctl instanceof wijmo.input.DropDown) {
- this._ctl.isDroppedDown = true;
- this._ctl.dropDown.focus();
- }
- }, 50);
-
- }
- // close the custom editor, optionally saving the edits back to the grid
- _closeEditor(saveEdits) {
- if (this._rng) {
- var flexGrid = this._grid, ctl = this._ctl, host = ctl.hostElement;
- // raise grid's cellEditEnding event
- var e = new wijmo.grid.CellEditEndingEventArgs(flexGrid.cells, this._rng);
- flexGrid.onCellEditEnding(e);
- // save editor value into grid
- if (saveEdits) {
- /**
- 这里加一个判断,实现下拉tree回填逻辑
- */
- if(!wijmo.isUndefined(ctl['checkedItems'])){
- var vals = ctl['checkedItems'];
- if(vals.length > 0){
- var value = "";
- vals.forEach(function(item){
- if(item.header){
- value += item.header + ","
- }
- })
- if(value.length > 0){
- value = value.substring(0, value.length-1);
- }
- this._grid.setCellData(this._rng.row, this._rng.col, value);
- }else{
- this._grid.setCellData(this._rng.row, this._rng.col, "");
- }
- }else if (!wijmo.isUndefined(ctl['value'])) {
- this._grid.setCellData(this._rng.row, this._rng.col, ctl['value']);
- }
- else if (!wijmo.isUndefined(ctl['text'])) {
- this._grid.setCellData(this._rng.row, this._rng.col, ctl['text']);
- }
- else {
- throw 'Can\'t get editor value/text...';
- }
- this._grid.invalidate();
- }
- // close editor and remove it from the DOM
- if (ctl instanceof wijmo.input.DropDown) {
- ctl.isDroppedDown = false;
- }
- host.parentElement.removeChild(host);
- this._rng = null;
- CustomGridEditor._isEditing = false;
- // raise grid's cellEditEnded event
- flexGrid.onCellEditEnded(e);
- }
- }
- // commit row edits, fire row edit end events (TFS 339615)
- _commitRowEdits() {
- var flexGrid = this._grid, ecv = flexGrid.editableCollectionView;
- this._closeEditor(true);
- if (ecv && ecv.currentEditItem) {
- var e = new wijmo.grid.CellEditEndingEventArgs(flexGrid.cells, flexGrid.selection);
- ecv.commitEdit();
- setTimeout(() => {
- flexGrid.onRowEditEnding(e);
- flexGrid.onRowEditEnded(e);
- flexGrid.invalidate();
- });
- }
- }
- }
- //
- document.readyState === 'complete' ? init() : window.onload = init;
- //
- function init() {
- //
- // create some random data
- var countries = [
- { header: 'Electronics', img: 'resources/electronics.png', items: [
- { header: 'Trimmers/Shavers' },
- { header: 'Tablets' },
- { header: 'Phones', img: 'resources/phones.png', items: [
- { header: 'Apple' },
- { header: 'Motorola' },
- { header: 'Nokia' },
- { header: 'Samsung' }
- ]},
- { header: 'Speakers' },
- { header: 'Monitors' }
- ]}
- ];
-
- var products = [
- { id: 0, name: 'Widget', unitPrice: 23.43 },
- { id: 1, name: 'Gadget', unitPrice: 12.33 },
- { id: 2, name: 'Doohickey', unitPrice: 53.07 }
- ];
- var data = [];
- var dt = new Date();
- for (var i = 0; i < 10; i++) {
- data.push({
- id: i,
- date: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60),
- time: new Date(dt.getFullYear(), i % 12, 25, i % 24, i % 60, i % 60),
- country: countries[0].header,
- product: products[Math.floor(Math.random() * products.length)].name,
- amount: Math.random() * 10000 - 5000,
- discount: Math.random() / 4
- });
- }
- //
- // grid with custom editors
- theGrid = new wijmo.grid.FlexGrid('#theGrid', {
- keyActionTab: 'CycleOut',
- autoGenerateColumns: false,
- itemsSource: data,
- columns: [
- { header: 'ID', binding: 'id', width: 40, isReadOnly: true },
- { header: 'Date', binding: 'date', format: 'd' ,"align":"right"},
- { header: 'Time', binding: 'time', format: 't' },
- { header: 'Country', binding: 'country' },
- { header: 'Product', binding: 'product' },
- { header: 'Amount', binding: 'amount', format: 'n2' }
- ],
- beginningEdit: function (s, e) {
- if (e.data.type == 'keypress' &&
- !document.getElementById('quickEdit').checked) {
- e.cancel = true;
- }
- },
- itemFormatter: function (panel, r, c, cell) {//单元格的GridPanel,从0开始——单元格的行、列索引、单元格的HTML元素
- var editRange = panel.grid.editRange;
- // if (panel.cellType == wijmo.grid.CellType.Cell && editRange && editRange.row === r && editRange.col === c) {
- // if(r==0){
- // // if (grid.columns[c].binding == 'country') {
- //
- // console.log(panel.cellType == wijmo.grid.CellType.Cell)
- // }
- // }
- }
- });
-
- new CustomGridEditor(theGrid, 3, wijmo.input.DropDownTree, {
- itemsSource: countries
- });
- new CustomGridEditor(theGrid, 4, wijmo.input.DropDownTree, {
- itemsSource: countries
- });
- // new CustomGridEditor(theGrid, 'amount', wijmo.input.InputNumber, {
- // format: 'n2',
- // step: 10
- // });
- //
- // create an editor based on a ComboBox
- // var multiColumnEditor = new CustomGridEditor(theGrid, 'product', wijmo.input.ComboBox, {
- // headerPath: 'name',
- // displayMemberPath: 'name',
- // itemsSource: products
- // });
- //
- // customize the ComboBox to show multiple columns
- // var combo = multiColumnEditor.control;
- // combo.listBox.formatItem.addHandler(function (s, e) {
- // e.item.innerHTML = '<table><tr>' +
- // '<td style="width:30px;text-align:right;padding-right:6px">' + e.data.id + '</td>' +
- // '<td style="width:100px;padding-right:6px"><b>' + e.data.name + '</b></td>' +
- // '<td style="width:80px;text-align:right;padding-right:6px">' +
- // wijmo.Globalize.format(e.data.unitPrice, 'c') +
- // '</td>' +
- // '</tr></table>';
- // });
- }
- </script>
- </body>
- </html>
复制代码
完整的实现,参考附件打包Demo。
|