I am still can’t get the column to show up with multirow.
I even stripped the code down to the bare minimum and tested, but still no column filters.
Also the hitTest() and formatItem issue is still occuring where the content will take up grid cell colspan 2 like it shoud, but will then overflow and wrap to be the next cell. This next cell should not contain the top cell’s content. I then moved the top colspan 2 cell below the two colpan 1 cells and the formatItem and hitTest() stops working.
Here is the full code:
<template>
<el-container>
<el-header class="formatHeader">
<el-breadcrumb separator-class="el-icon-arrow-right">
<el-breadcrumb-item><a href="/createpo">food List</a></el-breadcrumb-item>
<el-breadcrumb-item>Product list</el-breadcrumb-item>
</el-breadcrumb>
<el-tag size="medium">{{ foodName }} PO# {{ numOfFoods }}</el-tag>
</el-header>
<el-main>
<div class="toolBar">
<!-- Search Input -->
<div>
<el-input
type="text"
class="gobalSearchBar"
id="filter"
clearable
placeholder="search"
v-model="filterText">
<i slot="prefix" class="el-input__icon el-icon-search"></i>
</el-input>
</div>
<el-button class="exportExcel saveBtn" type="info" @click="saveColumnLayout()">Save Column Layout</el-button>
<el-button class="exportExcel loadBtn" type="primary" @click="loadColumnLayout()">Load Column Layout</el-button>
<!-- Export As Excel -->
<el-button class="exportExcel" type="primary" icon="el-icon-document" @click="exportExcel()">Export Excel</el-button>
<!-- Create PO Instructions-->
<el-alert
title="Create a PO:"
type="info"
description="Fill in Order Qty(s), and Click Save PO Button"
show-icon
:closable="false">
</el-alert>
<div class="savePo">
<!-- Save PO To DB -->
<el-button type="info" v-show="!save" @click="savePo" :disabled="btnClicked">Save PO</el-button>
<el-button type="success" v-show="save" @click="updatePo">Update PO</el-button>
</div>
</div>
<!-- Product Modal -->
<el-dialog v-for="info in foodInfo" :key="info.id" :title="info.title" :visible.sync="dialogFormVisible">
<wj-flex-grid
class="grid"
id="modalGrid"
control="grid"
:items-source="foodInfo"
:format-item="formatModalGrid"
selection-mode="None"
header-visibility="All">
<wj-flex-grid-column binding="oranges" header="Oranges"></wj-flex-grid-column>
<wj-flex-grid-column binding="apples" header="Apples"></wj-flex-grid-column>
<wj-flex-grid-column binding="crackers" header="Crackers"></wj-flex-grid-column>
<wj-flex-grid-column binding="proteins" header="Proteins" :data-map="proteins"></wj-flex-grid-column>
</wj-flex-grid>
<span slot="footer" class="dialog-footer">
<el-button type="primary" @click="dialogFormVisible=false">Close</el-button>
</span>
</el-dialog>
<!-- END OF MODAL -->
<!-- DataTable Grid -->
<wj-multi-row
class="grid expanded-groups"
id="theGrid"
control="grid"
:items-source="data"
:layout-definition="def"
:collapsed-headers="null"
:initialized="initGrid"
:allow-sorting="true"
:format-item="formatProduct"
selection-mode="Cell"
header-visibility="All">
<!-- Each Column Has A Filter -->
<wj-flex-grid-filter :initialized="initFil"></wj-flex-grid-filter>
</wj-multi-row>
<!-- End Grid -->
<!-- Pagination -->
<div class="btn-group pagination">
<button type="button" class="btn btn-default"
@click="data.moveToFirstPage()"
:disabled="data.pageIndex <= 0">
<span class="fas fa-fast-backward"></span>
</button>
<button type="button" class="btn btn-default"
@click="data.moveToPreviousPage()"
:disabled="data.pageIndex <= 0">
<span class="fas fa-step-backward"></span>
</button>
<button type="button" class="btn btn-default" disabled style="width:100px">
{{data.pageIndex + 1}}/{{data.pageCount}}
</button>
<button type="button" class="btn btn-default"
@click="data.moveToNextPage()"
:disabled="data.pageIndex >= data.pageCount - 1">
<span class="fas fa-step-forward"></span>
</button>
<button type="button" class="btn btn-default"
@click="data.moveToLastPage()"
:disabled="data.pageIndex >= data.pageCount - 1">
<span class="fas fa-fast-forward"></span>
</button>
<select name="pagesize" id="pageSize" v-model="pageSize">
<option :value=25>25</option>
<option value=50>50</option>
<option value=100>100</option>
</select>
</div>
</el-main>
</el-container>
</template>
<script>
import * as wjCore from 'wijmo/wijmo'
import * as wjGrid from 'wijmo/wijmo.grid'
import * as wjGridXlsx from 'wijmo/wijmo.grid.xlsx';
import * as wjInput from 'wijmo/wijmo.input'
import 'wijmo/wijmo.vue2.input'
import 'wijmo/wijmo.vue2.grid.filter'
import 'wijmo/wijmo.vue2.grid.multirow'
import * as wjMultirow from "wijmo/wijmo.grid.multirow"
import { CollectionView } from 'wijmo/wijmo';
export default {
name: 'PoProducts',
data () {
let foodsMap = [
{ value: 1, text: "example1" },
{ value: 2, text: "example2" },
{ value: 3, text: "example3" },
{ value: 4, text: "example4" }
]
let proteinsMap = [
{ value: 0, text: "test1" },
{ value: 1, text: "test2" },
{ value: 2, text: "test3" },
{ value: 3, text: "test4" }
]
return {
grid: null,
data: new wjCore.CollectionView(),
// Define Grouped Columns Layout
def: [
{
header: '', colspan: 1, cells: [
{binding: 'oranges', header: 'Oranges', colspan: 1}
]
},
{
header: 'Food Info', colspan: 2, cells: [
{binding: 'lemons', header: 'lemons', colspan: 1},
{binding: 'apples', header: 'apples', colspan: 1},
{binding: 'bananas', header: 'Banans', colspan: 2, wordWrap: true, multiLine: true},
]
},
{
header: 'Food Details', colspan: 2, cells: [
{binding: 'crackers', header: 'Crackers', colspan: 1},
{binding: 'kiwis', header: 'Kiwis', colspan: 1},
{binding: 'strawberries', header: 'Strawberries', colspan: 2},
]
},
{
header: 'Category', colspan: 2, cells: [
{binding: 'fruit', header: 'Fruit', colspan: 2},
{binding: 'vegetables', header: 'Veggies', colspan: 1},
{binding: 'carbs', header: 'Carbs', colspan: 1},
]
}
],
foods: new wjGrid.DataMap(foodsMap, "value", "text"),
foodName: 'Food Name Will Go Here',
numOfFoods: 0,
foodFormData: [],
isVegetable: false,
save: false,
btnClicked: false,
pageSize: 100,
filterText: '',
filterRx: null,
proteins: new wjGrid.DataMap(proteinsMap, "value", "text"),
dialogFormVisible: false,
foodInfo: []
}
},
mounted(){
// Get All Products From Clicked food Name
axios.get('/get_foods/'+food)
.then((response) => {
(this.data = new wjCore.CollectionView(response.data.food_items, {
pageSize: this.pageSize
}), this.data.filter = this.filter)
.then(this.numOfFoods = response.data.numOfFoods);
});
},
methods: {
initGrid(grid) {
// grid.autoSizeColumns();
this.grid = grid;
// SET food NAME
let foodNum = food;
let foodNameIndex = this.food.collectionView.sourceCollection.filter(o=>Object.values(o).includes(foodNum));
let foodName = foodNameIndex[0].text;
this.foodName = foodName;
// CLICK ON PRODUCT TITLE TO OPEN MODAL
grid.hostElement.addEventListener('click', (e)=>{
let htInfo = grid.hitTest(e);
let panel = htInfo.panel,
col = htInfo.panel.columns[htInfo.col],
row = htInfo.panel.rows[htInfo.row];
if(!htInfo.panel || htInfo.panel.cellType!=wjGrid.CellType.Cell){
return;
}
if(col.binding != 'oranges'){
return;
}
this.foodInfo = [{title: row.dataItem.oranges, uad: row.dataItem.uad, apples: row.dataItem.apples, crackers: row.dataItem.crackers}];
// trigger Product Modal
this.dialogFormVisible = true;
});
// ONLY ALLOW NUMERIC VALUES FOR Oranges
grid.prepareCellForEdit.addHandler((s, e) => {
let col = e.panel.columns[e.col];
if (col.binding != "apples") {
return;
}
wjCore.setAttribute(s.activeEditor, "type", "number");
s.activeEditor.addEventListener("keydown", e => {
if (e.keyCode == 69) {
e.preventDefault();
}
})
// set the value again, since prev value was of text type
let val = e.panel.getCellData(e.row, e.col);
s.activeEditor.value = val;
});
// UPDATE COMBO STYLING FLAG
this.updateFlags(grid.collectionView.items, 'lemons');
// BUILD PO FORM DATA
grid.cellEditEnded.addHandler((s, e) => {
let col = e.panel.columns[e.col];
let row = e.panel.rows[e.row];
let qtys = [];
let poData = [];
if ( col.binding != "oranges" && col.binding != "apples" ) {
return;
}
this.data.sourceCollection.forEach(row => {
if(row.oranges != undefined && row.oranges > 0) {
// build condition to allow combo
qtys.push(row.oranges);
// add or remove row data to and from foodFormData array
foodData.push({
food_number: this.numOfFoods,
food: row.food,
food_lemons: row.food_lemons,
seller_lemons: row.lemons,
price: row.crackers,
crate: row.oranges,
bucket: row.lemons});
} else {
foodData.splice(row, 0);
}
});
this.foodFormData = poData;
// sort desc when order qty is entered
if (col.binding == 'oranges') {
this.applySort();
}
// only show make combo column if Milwaukee
if( row.dataItem.food == 4 && qtys.length > 1 ) {
this.isVegetable = true;
} else {
this.isVegetable = false;
}
// if lemmons made add cell styling
this.updateFlags(s.collectionView.items, 'lemmons');
// end cell edit ended event
});
// IF FLAG THEN ADD CLASS FOR lemmon CELL STYLING
grid.formatItem.addHandler((s, e) => {
if (e.panel != s.cells) {
return;
}
let col = s.columns[e.col],
row = s.rows[e.row];
if (col.binding != "lemmons") {
return;
}
let dataItem = row.dataItem;
// compare to other values too
if (dataItem.lemmons != undefined && dataItem.lemmons !== '' && dataItem["_flag"] == "green") {
wjCore.addClass(e.cell, "green-cell");
} else if (dataItem.lemmons != undefined && dataItem.lemmons !== '') {
wjCore.addClass(e.cell, "red-cell");
}
});
// COLUMN SELECTION
// format top left to display edit icon
grid.formatItem.addHandler((s,e)=>{
if(s.topLeftCells == e.panel){
e.cell.innerHTML = '<span class="wj-glyph-pencil col-picker-icon" style="color: orange"></span>';
}
});
// create the column picker
var theColumnPicker = new wjInput.ListBox(document.createElement('div'), {
itemsSource: grid.columns,
checkedMemberPath: 'visible',
displayMemberPath: 'header',
lostFocus() {
wjCore.hidePopup(theColumnPicker.hostElement);
}
});
wjCore.addClass(theColumnPicker.hostElement, 'col-picker');
// show column picker
let ref = grid.hostElement.querySelector('.wj-topleft');
grid.hostElement.addEventListener('mousedown', function (e) {
if (wijmo.hasClass(e.target, 'col-picker-icon')) {
wjCore.showPopup(theColumnPicker.hostElement, ref, false, true, false);
theColumnPicker.focus();
e.preventDefault();
}
}, true);
// end initGrid method
},
applySort() {
if (!this.grid) {
return;}
// create sort
let sd = new wjCore.SortDescription('oranges', false);
// clear previous sort
this.grid.collectionView.sortDescriptions.clear();
// push collectionView's sortDescription property
this.grid.collectionView.sortDescriptions.push(sd);
},
updateFlags(data, prop) {
data.forEach(item => {
item["_flag"] = "red";
});
for (let i = 0; i < data.length - 1; i++) {
for (let j = i + 1; j < data.length; j++) {
if (data[i][prop] == data[j][prop]) {
data[i]["_flag"] = data[j]["_flag"] = "green";
}
}
}
},
saveColumnLayout() {
let grid = wijmo.Control.getControl("#theGrid");
localStorage['columns'] = grid.columnLayout;
this.$message({
message: 'Column layout saved.',
type: 'success'
})
},
loadColumnLayout() {
let grid = wijmo.Control.getControl("#theGrid"),
columnLayout = localStorage['columns'];
if (columnLayout) {
grid.columnLayout = columnLayout;
}
},
formatProduct (s, e) {
if( e.panel == s.cells && s.columns[e.col].binding == 'uad' ) {
// display food images
e.cell.innerHTML = wijmo.format(
'<img src="{uad}">',
s.rows[e.row].dataItem);
} else if( e.panel == s.cells && s.columns[e.col].binding == 'crackers' ) {
// display food name as a link
e.cell.innerHTML = wijmo.format(
'<a href="#">{crackers}</a>',
s.rows[e.row].dataItem);
} else if( e.panel == s.cells && s.columns[e.col].binding == 'lemmons' ) {
if( s.rows[e.row].dataItem.oranges == undefined
|| s.rows[e.row].dataItem.oranges == ''
|| s.rows[e.row].dataItem.oranges == 0 ) {
e.cell.innerText = wijmo.format( '', s.rows[e.row].dataItem );
}
}
},
formatModalGrid (s,e) {
if ( e.panel == s.cells && s.columns[e.col].binding == 'uad' ) {
// display food image
e.cell.innerHTML = wijmo.format(
'<img src="{uad}">',
s.rows[e.row].dataItem);
let grid = wijmo.Control.getControl(document.getElementById('modalGrid'));
grid.rows.forEach(row => {
return row.size = 80;
})
}
},
savePo() {
this.btnClicked = true;
axios.post('/make_food_list', this.foodFormData)
.then(response => {
if(response.status == 200) {
this.foodMade()
return this.save = true;
}
})
.catch(function (error) {
alert(error);
});
},
updateFoodForm() {
axios.post('/update_food_list', this.foodFormData)
.then(response => {
if(response.status == 200) {
this.foodMade()
}
})
.catch(function (error) {
alert(error);
});
},
foodMade() {
this.$notify({
title: 'Success',
message: 'Food List Saved',
type: 'success'
});
},
foodListError() {
this.$notify({
title: 'Error',
message: 'List not Saved',
type: 'error'
});
},
exportExcel() {
if(this.grid){
wjGridXlsx.FlexGridXlsxConverter.save(this.grid, null, 'sample.xlsx');
}
},
initFil(fil) {
//save filter instance for later use
this.flexFilter = fil;
fil.filterApplied.addHandler((s,e)=>{
setTimeout(() => {
fil.grid.collectionView.filter = this.filter;
});
});
},
filter(item) {
// passes both filters
return this.itemPassesDefaultFilter(item) && this.itemPassesCustomFilter(item);
},
itemPassesDefaultFilter(item) {
// no filter, thus filter passed
if (!this.flexFilter) {
return true;
}
let filCols = this.flexFilter.filterColumns;
if (!filCols) {
filCols = this.flexFilter.grid.columns.map(col => col.binding);
}
for (let i = 0; i < filCols.length; i++) {
let colBinding = filCols[i];
let colFil = this.flexFilter.getColumnFilter(colBinding);
if (!colFil.apply(item)) {
// failed FlexGridFilter
return false;
}
}
// all column filters passed
return true;
},
itemPassesCustomFilter(item) {
return this.filterRx == null || this.filterRx.test(item.apples) || this.filterRx.test(item.lemons);
}
},
created: function() {
this.data.filter = this.filter;
this.$watch('filterText', function() {
this.filterRx = this.filterText ? new RegExp(this.filterText, 'i') : null;
this.data.refresh();
})
},
watch: {
pageSize(val,oldVal) {
this.data.pageSize=parseInt(val);
}
}
}
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
.el-header {
display: flex;
align-items: center;
font-size: 14px;
height: 30px !important;}
.el-breadcrumb {
margin-right: 20px;}
.el-button {
font-weight: 200;}
.el-tag {
background-color: #546F87;
border-color: #546F87;
color: #FFF;
font-weight: 200;
font-size: 14px;}
.el-main .wj-control {
display: inline-block;
vertical-align: top;
width: 100%;
height: auto;}
.toolBar {
display: flex;
align-items: baseline;}
.el-alert {
margin-left: 20px;
max-width: 500px;}
.el-input {
min-width: 300px;}
.wj-cell.no-padding-cell {
padding: 0px!important;}
.hidden { display: none; }
.exportExcel {
margin: 0 0 0 20px;
margin-bottom: 20px;}
s .grid {
font-size: 14px;
height: auto;
max-height: 600px;}
h1, h2 {
font-weight: normal;}
ul {
list-style-type: none;
padding: 0;}
li {
display: inline-block;
margin: 0 10px;}
.fa-fast-forward,
.fa-step-forward,
.fa-fast-backward,
.fa-step-backward {
color: #333;}
</style>
<style>
.expanded-groups .wj-cell.wj-group-header {
font-weight: 200;
background-color: #546F87;
border-color: transparent;
color: #FFF;
}
.wj-state-multi-selected{
background: #546F87 !important;
opacity: 0.885;
}
.wj-state-selected{
background: #0074D9 !important;
}
/* div[wj-part="div-valuses"]{
max-height:200px;
} */
.wj-cell.green-cell:not(.wj-header):not(.wj-group):not(.wj-state-selected):not(.wj-state-multi-selected) {
background-color: rgba(46,139,87,0.2);
/* color: #FFF; */
}
.wj-cell.red-cell:not(.wj-header):not(.wj-group):not(.wj-state-selected):not(.wj-state-multi-selected) {
background-color: rgba(255,0,1,0.9);
color: #FFF;
}
.pagination, .pagination button {
font-size: 13px;}
</style>