ag-grid: Grouped Footers don't work with Server Side Row Model, need a workaround - ag-grid

ag-grid supports showing a footer in each group, and even subgroups. However, that page does not mention that it doesn't worth with SSRM.
Would have been nice to know ahead, but now I'm looking for a workaround. Any awful, terrible way to show some custom information at the bottom of each group, including nested groups. Disabling SSRM is not an option, and the information must positioned at the bottom of the group, nowhere else.
I've tried a bunch of things. Some of these might still work, but are so awful it's hard to tell:
Using a pinned bottom row -> doesn't work with lots of groups.
Adding a DOM element after everything has rendered -> ag grid positioning is too aggressive, you'd have to force the grid out of the way.
Hooking into the query to return an extra row -> requires modifying too many query components.
Adding a fake row directly into the source data -> some things are too awful.
Using a custom full-width row to hijack the last row in a group and add a fake "row" -> maybe?
If you have even a suggestion without a solution, I would be happy to hear it.

I have successfully implemented a total footer row for SSR Model and I think it should be possible for grouped rows as well. I will share a complete example with you including implementing the checkbox feature which is also normally not possible for SSR Model. Anyways, here's my generic solution. (It's not possible to answer that at once because of body limitation, I will split it into 3 answers.
We Begin
PART 1
FINAL RESULT
HELPERS
myApp.js file structure
var gridOptions = null;
var SSRMGridOptions = null;
var SSRMGridResized = false;
var mobileGrid = false;
var fakeServerMethods = [];
var fakeServerData = [];
var fieldsToBeFormattedForAgg = [];
var fakeServerDataLoaded = false;
var fakeServerSelectedRows = [];
var firstDataRendered = false;
var Transactions = function () {
// I will share the necessary content
};
How we call this file in the footer
<script src="<?php echo $this->config->item('backend_theme_path'); ?>/app/js/myApp.js<?php echo CACHE_VERSION; ?>" type="text/javascript"></script>
Main function for SSRM Ag-Grid
this.SSRMAgGrid = function SSRMAgGrid(columnDefs, SSRMGridOptionsIncluded, jsonData, jsonUrl, excludedFields = [], useLocalStorage = true, divID='#myGrid', is_modal = false) {
SSRMGridOptionsIncluded = typeof SSRMGridOptionsIncluded !== 'undefined' ? SSRMGridOptionsIncluded : {};
jsonData = typeof jsonData !== 'undefined' ? jsonData : {};
if (!SSRMGridOptionsIncluded.doNotGenerateSelectBox) {
columnDefs.unshift({
field: 'checkbox',
maxWidth: 50,
headerName: '', checkboxSelection: true,
headerComponent: App.CustomSelectBoxHeader,
headerComponentParams: {showMe: true},
menuTabs: ['generalMenuTab', 'columnsMenuTab']
});
}
columnDefs.forEach(function(columnDef, index) {
if (columnDef.aggFunc) {
switch (columnDef.aggFunc) {
case 'sum':
fieldsToBeFormattedForAgg.push(columnDef.field);
break;
case 'avg':
fieldsToBeFormattedForAgg.push(columnDef.field);
break;
}
}
if (columnDef.rowGroup) {
}
});
if (window.innerWidth <= 768) {
mobileGrid = true;
}
if (mobileGrid) {
columnDefs.forEach(function(columnDef, index) {
if (columnDef.pinned) {
delete columnDef.pinned;
}
if (columnDef.cellStyle) {
delete columnDef.cellStyle;
}
if (columnDef.pinnedRowCellRenderer) {
delete columnDef.pinnedRowCellRenderer;
}
});
}
SSRMGridOptions = {
debug: false,
columnDefs: columnDefs,
defaultColDef: {
sortable: true,
filter: true,
resizable: true,
},
headerHeight: 45,
rowHeight: 35,
enableRangeSelection: true,
rowSelection: 'multiple',
groupIncludeFooter: true,
animateRows: true,
localeText: AG_GRID_LANGUAGE_TEXT,
columnTypes: {
number: { filter: 'agNumberColumnFilter' },
},
rowModelType: 'serverSide',
serverSideStoreType: 'partial',
rowStyle: {fontFamily: 'Comic Sans MS, Comic Sans, Verdana', color: 'darkslategray'},
onRowSelected: params => {
var element = document.getElementById('customSelectBox');
if (params.node.selected) {
fakeServerSelectedRows.push(params.data);
}
else {
fakeServerSelectedRows = fakeServerSelectedRows.filter(row => row.id !== params.data.id);
}
if (params.node.selected) {
if (App.isFakeServerDataLoaded()) {
if (SSRMGridOptions.api.getDisplayedRowCount() < App.getFakeServerDataCount()) {
if (!element.classList.contains('ag-indeterminate')) {
element.classList.add('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
else {
if (SSRMGridOptions.api.getSelectedRows().length === App.getFakeServerDataCount()) {
if (element.classList.contains('ag-indeterminate')) {
element.classList.remove('ag-indeterminate');
}
if (!element.classList.contains('ag-checked')) {
element.classList.add('ag-checked');
}
}
else {
if (!element.classList.contains('ag-indeterminate')) {
element.classList.add('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
}
}
else {
if (!element.classList.contains('ag-indeterminate')) {
element.classList.add('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
}
else {
if (App.isFakeServerDataLoaded()) {
if (SSRMGridOptions.api.getDisplayedRowCount() < App.getFakeServerDataCount()) {
if (SSRMGridOptions.api.getSelectedRows().length === 0) {
if (element.classList.contains('ag-indeterminate')) {
element.classList.remove('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
else {
if (!element.classList.contains('ag-indeterminate')) {
element.classList.add('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
}
else {
if (SSRMGridOptions.api.getSelectedRows().length === 0) {
if (element.classList.contains('ag-indeterminate')) {
element.classList.remove('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
else {
if (!element.classList.contains('ag-indeterminate')) {
element.classList.add('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
}
}
else {
if (SSRMGridOptions.api.getSelectedRows().length === 0) {
if (element.classList.contains('ag-indeterminate')) {
element.classList.remove('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
else {
if (!element.classList.contains('ag-indeterminate')) {
element.classList.add('ag-indeterminate');
}
if (element.classList.contains('ag-checked')) {
element.classList.remove('ag-checked');
}
}
}
}
},
onGridReady: params => {
$('input[name=searchGrid]').val(searchInListValue);
SSRMGridOptions.api.setQuickFilter(searchInListValue);
var locId = location.href.replace('#','');
locId = locId + '#itemsGrid';
var getSort = $.parseJSON(localStorage.getItem(locId));
setTimeout(() => {
params.columnApi.applyColumnState({state: getSort, applyOrder: true});
}, 3000);
},
onModelUpdated: params => {
if (fakeServerSelectedRows.length > 0) {
SSRMGridOptions.api.forEachNode((node, index) => {
if (node.data) {
if (fakeServerSelectedRows.filter(row => row.id === node.data.id).length > 0) {
var row = fakeServerSelectedRows.filter(row => row.id === node.data.id);
node.setSelected(true);
} else {
node.setSelected(false);
}
}
});
}
var savedSort = params.columnApi.getColumnState();
var sortState = savedSort
.filter(function (s) {
return s.sort != null;
})
.map(function (s) {
return { colId: s.colId, sort: s.sort, sortIndex: s.sortIndex };
});
var countRows = params.api.getDisplayedRowCount();
if (!fakeServerDataLoaded) {
countRows--;
}
if (!mobileGrid) {
if (!SSRMGridOptions.doNotGenerateTotalRow) {
let pinnedBottomData = App.generateSSRMPinnedTotalRow(columnDefs, columns, fakeServerData.length, sortState);
SSRMGridOptions.api.setPinnedBottomRowData(pinnedBottomData);
}
}
},
onFirstDataRendered: params => {
firstDataRendered = true;
setTimeout(() => {
App.autoSizeAll(SSRMGridOptions);
if (fieldsToBeFormattedForAgg.length > 0) {
setTimeout(() => {
SSRMGridOptions.api.expandAll();
setTimeout(() => {
App.autoSizeAll(SSRMGridOptions);
}, 1000);
}, 500);
}
}, 1000);
},
onDisplayedColumnsChanged: params => {
if(searchInListValue) {
$('input[name=searchGrid]').val(searchInListValue);
SSRMGridOptions.api.setQuickFilter(searchInListValue);
}
if (firstDataRendered) {
App.ColumnStateToLocalStorage(params);
}
},
onBodyScroll: params => {
var bottom_px = SSRMGridOptions.api.getVerticalPixelRange().bottom;
var grid_height = SSRMGridOptions.api.getDisplayedRowCount() * SSRMGridOptions.api.getSizesForCurrentTheme().rowHeight;
if(bottom_px == grid_height)
{
}
},
onColumnResized: params => {
if (firstDataRendered) {
App.ColumnStateToLocalStorage(params);
}
},
onSortChanged: params => {
var savedSort = params.columnApi.getColumnState();
if(savedSort != ''){
var locId = location.href.replace('#','');
locId = locId + '#itemsGrid';
localStorage.setItem(locId,JSON.stringify(savedSort));
}
}
};
if(SSRMGridOptionsIncluded != null){
$.each(SSRMGridOptionsIncluded,(key,value) => {
SSRMGridOptions[key] = value;
});
}
$('input[name=searchGrid]').on('keyup', function (event) {
if (event.keyCode == 13 || event.keyCode == 16 || event.keyCode == 17 || event.keyCode == 18 || event.keyCode == 91) {
event.preventDefault();
return false;
}
customParams.quickFilterValue = $(this).val();
SSRMGridOptions.api.onFilterChanged();
});
var fakeServer;
var dataSource;
var customParams = {};
customParams.quickFilterValue = '';
fakeServerMethods = [];
fakeServerData = [];
firstDataRendered = false;
columnDefs.forEach(function(columnDef, index) {
if (columnDef.filter) {
if (columnDef.filterParams) {
if (columnDef.filterParams.fakeServerSql) {
var sql = columnDef.filterParams.fakeServerSql
var method = function () {
var fieldName = columnDef.field;
return alasql(sql, [fakeServerData]).map(function (x) {
return x[fieldName];
});
}
columnDef.filterParams.values = params => {
setTimeout(function () {
params.success(method());
}, 500);
};
fakeServerMethods.push({
name: columnDef.field,
method: method,
});
}
}
}
});
new agGrid.Grid(document.querySelector(divID), SSRMGridOptions);
SSRMGridOptions.api.showLoadingOverlay();
this.requestPostAsync(jsonData, jsonUrl, json => {
setTimeout(() => {
console.log(json.data)
fakeServerData = json.data;
if (fieldsToBeFormattedForAgg.length > 0) {
fakeServerData = fakeServerData.map(data => {
var temp = Object.assign({}, data);
for (var k = 0; k < fieldsToBeFormattedForAgg.length; k++) {
temp[fieldsToBeFormattedForAgg[k]] = Number(data[fieldsToBeFormattedForAgg[k]]);
}
return temp;
});
}
console.log(fakeServerData)
console.log('Fake Server Data Loaded');
fakeServerSelectedRows = [];
SSRMGridResized = false;
fakeServer = new this.fakeServer(columnDefs, fakeServerData);
dataSource = new this.serverSideDataSource(fakeServer, customParams);
SSRMGridOptions.api.setServerSideDatasource(dataSource);
if (!mobileGrid) {
if (!SSRMGridOptions.doNotGenerateTotalRow) {
var savedSort = SSRMGridOptions.columnApi.getColumnState();
var sortState = savedSort
.filter(function (s) {
return s.sort != null;
})
.map(function (s) {
return { colId: s.colId, sort: s.sort, sortIndex: s.sortIndex };
});
let pinnedBottomData;
if (json.data.length > 100) {
fakeServerDataLoaded = false;
pinnedBottomData = App.generateSSRMPinnedTotalRow(columnDefs, columns, json.data.length, sortState);
} else {
fakeServerDataLoaded = true;
pinnedBottomData = App.generateSSRMPinnedTotalRow(columnDefs, columns, json.data.length, sortState);
}
SSRMGridOptions.api.setPinnedBottomRowData(pinnedBottomData);
}
}
if (useLocalStorage) {
if (typeof localStorage !== 'undefined') {
var locId = location.href.replace('#','');
locId = locId + '#itemsGrid';
if (typeof localStorage[locId] !== 'undefined') {
var columnState = JSON.parse(localStorage[locId]);
SSRMGridOptions.columnApi.setColumnState(columnState);
}
}
}
SSRMGridOptions.api.hideOverlay();
}, 500);
});
HELPER FUNCTIONS
this.getFakeServerDataCount = function getFakeServerDataCount() {
return fakeServerData.length;
};
this.CustomSelectBoxHeader = function CustomSelectBoxHeader() {};
this.CustomSelectBoxHeader.prototype.init = function (params) {
var template = '';
if (params.showMe) {
template =
'<input type="checkbox" class="ag-input-field-input ag-checkbox-input" id="customSelectBox" style="height: 20px; width: 20px;" />';
}
else {
template =
'<span>' + params.displayName + '</span>';
}
this.params = params;
this.eGui = document.createElement('div');
var eGui = this.eGui;
eGui.setAttribute('role', 'presentation');
eGui.classList.add('ag-header-select-all');
eGui.classList.add('ag-labeled');
eGui.classList.add('ag-label-align-right');
eGui.classList.add('ag-checkbox');
eGui.classList.add('ag-input-field');
var wrapper = document.createElement('div');
wrapper.id = "customSelectBox";
wrapper.setAttribute('role', 'presentation');
wrapper.classList.add('ag-wrapper');
wrapper.classList.add('ag-input-wrapper');
wrapper.classList.add('ag-checkbox-input-wrapper');
wrapper.innerHTML = template;
this.eSelectAllButton = wrapper.querySelector('#customSelectBox');
this.onChangeListener = this.selectAll.bind(this, this.eSelectAllButton);
this.eSelectAllButton.addEventListener('change', this.onChangeListener);
eGui.append(wrapper);
};
this.CustomSelectBoxHeader.prototype.selectAll = function (element) {
if (App.isFakeServerDataLoaded()) {
if (SSRMGridOptions.api.getDisplayedRowCount() < App.getFakeServerDataCount()) {
if (SSRMGridOptions.api.getSelectedRows().length < SSRMGridOptions.api.getDisplayedRowCount()) {
this.params.api.forEachNode(function (node) {
node.setSelected(true);
});
this.mark.bind(this, element, 'indeterminate');
}
else {
this.params.api.deselectAll();
this.mark.bind(this, element, '');
}
}
else {
if (SSRMGridOptions.api.getSelectedRows().length < App.getFakeServerDataCount()) {
this.params.api.forEachNode(function (node) {
node.setSelected(true);
});
this.mark.bind(this, element, 'checked');
} else {
this.params.api.deselectAll();
this.mark.bind(this, element, '');
}
}
}
else {
if (SSRMGridOptions.api.getSelectedRows().length < SSRMGridOptions.api.getDisplayedRowCount() - 1) {
this.params.api.forEachNode(function(node) {
node.setSelected(true);
});
this.mark.bind(this, element, 'indeterminate');
}
else {
this.params.api.deselectAll();
this.mark.bind(this, element, '');
}
}
};
this.CustomSelectBoxHeader.prototype.mark = function (element, status) {
if (status === '') {
if (element.parentNode.classList.contains('ag-indeterminate')) {
element.parentNode.classList.remove('ag-indeterminate');
}
if (element.parentNode.classList.contains('ag-checked')) {
element.parentNode.classList.remove('ag-checked');
}
}
else if (status === 'indeterminate') {
if (!element.parentNode.classList.contains('ag-indeterminate')) {
element.parentNode.classList.add('ag-indeterminate');
}
if (element.parentNode.classList.contains('ag-checked')) {
element.parentNode.classList.remove('ag-checked');
}
}
else if (status === 'checked') {
if (element.parentNode.classList.contains('ag-indeterminate')) {
element.parentNode.classList.remove('ag-indeterminate');
}
if (!element.parentNode.classList.contains('ag-checked')) {
element.parentNode.classList.add('ag-checked');
}
}
};
this.CustomSelectBoxHeader.prototype.getGui = function () {
return this.eGui;
};
this.CustomSelectBoxHeader.prototype.destroy = function () {
this.eSelectAllButton.removeEventListener('click', this.onChangeListener);
}
this.isFakeServerDataLoaded = function isFakeServerDataLoaded() {
return fakeServerDataLoaded;
};
this.generateSSRMPinnedTotalRow = function generateSSRMPinnedTotalRow(columnDefs, columns, total, sortState) {
var arr = [];
var obj = {};
var totalRows = [];
columnDefs.forEach(function(columnDef, index) {
if (columnDef.filterParams) {
if (columnDef.filterParams.totalRow) {
totalRows.push(columnDef.field)
}
}
});
var serverData = fakeServerData.slice();
if (sortState !== null) {
if (sortState.length > 0) {
sortState.sort((a, b) => a.sortIndex - b.sortIndex);
var sortArgs = [];
sortArgs[0] = serverData;
sortState.forEach(item => {
if (item.sort === 'desc') {
sortArgs.push([item.colId, true]);
}
else {
sortArgs.push(item.colId);
}
});
App.objSort.apply(this, sortArgs);
}
}
for (var i = 0; i < columns.length; i++) {
if (!('checkboxSelection' in columns[i]) && !('hide' in columns[i])) {
var key = columns[i].field;
if (Object.keys(obj).length === 0) {
obj[key] = $_TOTAL_ROW_COUNT;
}
else if (Object.keys(obj).length === 1) {
var cnt = 0;
SSRMGridOptions.api.forEachNode(function (node) {
cnt++;
});
obj[key] = total;
}
else {
obj[key] = undefined;
if (totalRows.includes(key)) {
var sumOfAll = serverData.reduce((total, obj) => parseFloat(obj[key]) + parseFloat(total), 0);
var sum = 0;
SSRMGridOptions.api.forEachNode(function (node) {
if ('data' in node) {
sum += parseFloat(node.data[key]);
}
});
obj[key] = sumOfAll;
}
}
}
}
arr.push(obj);
return arr;
};
this.ColumnStateToLocalStorage = function ColumnStateToLocalStorage(e, detailGrid) {
setTimeout(_ => {
if (typeof localStorage !== 'undefined') {
var locId = location.href.replace('#', '');
if (!detailGrid) {
locId = locId + '#itemsGrid';
} else {
locId = locId + '#itemsGridDetail';
}
var state = e.columnApi.getColumnState();
localStorage[locId] = JSON.stringify(state);
} else {
App.autoSizeAll(e);
}
}, 750);
};
this.serverSideDataSource = function serverSideDataSource(fakeServer, customParams) {
return {
getRows: function (params) {
console.log('[Datasource] - rows requested by grid: ', params.request);
params.request.customParams = customParams
var response = fakeServer.getData(params.request);
fakeServerDataLoaded = response.lastRow !== -1;
setTimeout(function () {
if (response.success) {
params.success({
rowData: response.rows,
rowCount: response.lastRow,
});
} else {
params.fail();
}
}, 500);
},
};
};

PART 2
We keep going..
this.fakeServer = function fakeServer(columnDefs, fakeServerData) {
alasql.options.cache = false;
var obj = {
getData: function (request) {
var results = executeQuery(request);
console.log('results')
console.log(results)
return {
success: true,
rows: results,
lastRow: getLastRowIndex(request, results),
};
},
};
columnDefs.forEach(function(columnDef, index) {
if (columnDef.filter) {
if (columnDef.filterParams) {
if (columnDef.filterParams.fakeServerSql) {
var method = fakeServerMethods.find(fakeServerMethod => fakeServerMethod.name === columnDef.field).method;
var methodName = 'get_' + columnDef.field;
obj[methodName] = method;
}
}
}
});
return obj;
function executeQuery(request) {
var sql = buildSql(request);
console.log('[FakeServer] - about to execute query:', sql);
var rowGroupCols = request.rowGroupCols;
var groupKeys = request.groupKeys;
if (!isDoingGrouping(rowGroupCols, groupKeys)) {
return alasql(sql, [fakeServerData]);
}
var groupsToUse = request.rowGroupCols.slice(
groupKeys.length,
groupKeys.length + 1
);
var groupColId = groupsToUse[0].id;
var childCountResult = executeGroupChildCountsQuery(request, groupColId);
return alasql(sql, [fakeServerData]).map(function (group) {
group['childCount'] = childCountResult[group[groupColId]];
return group;
});
}
function executeGroupChildCountsQuery(request, groupId) {
var sql = interpolate(
'SELECT {0} FROM ? pivot (count({0}) for {0})' + whereSql(request),
[groupId]
);
console.log(
'[FakeServer] - about to execute group child count query:',
sql
);
return alasql(sql, [fakeServerData])[0];
}
function buildSql(request) {
return (
selectSql(request) +
' FROM ?' +
whereSql(request) +
groupBySql(request) +
orderBySql(request) +
limitSql(request)
);
}
function selectSql(request) {
var rowGroupCols = request.rowGroupCols;
var valueCols = request.valueCols;
var groupKeys = request.groupKeys;
if (isDoingGrouping(rowGroupCols, groupKeys)) {
var rowGroupCol = rowGroupCols[groupKeys.length];
var colsToSelect = [rowGroupCol.id];
valueCols.forEach(function (valueCol) {
colsToSelect.push(
valueCol.aggFunc + '(' + valueCol.id + ') AS ' + valueCol.id
);
});
return 'SELECT ' + colsToSelect.join(', ');
}
return 'SELECT *';
}
function isDoingGrouping(rowGroupCols, groupKeys) {
return rowGroupCols.length > groupKeys.length;
}
function createFilterSql(mapper, key, item) {
if (item.operator) {
var condition1 = mapper(key, item.condition1);
var condition2 = mapper(key, item.condition2);
return '(' + condition1 + ' ' + item.operator + ' ' + condition2 + ')';
}
return mapper(key, item);
}
function textFilterMapper(key, item) {
switch (item.type) {
case 'equals':
return key + " = '" + item.filter + "'";
case 'notEqual':
return key + "' != '" + item.filter + "'";
case 'contains':
return key + " LIKE '%" + item.filter + "%'";
case 'notContains':
return key + " NOT LIKE '%" + item.filter + "%'";
case 'startsWith':
return key + " LIKE '" + item.filter + "%'";
case 'endsWith':
return key + " LIKE '%" + item.filter + "'";
default:
console.log('unknown text filter type: ' + item.type);
}
}
function numberFilterMapper(key, item) {
switch (item.type) {
case 'equals':
return 'CAST(' + key + ' AS DECIMAL(10,2)) = ' + item.filter.toFixed(2);
case 'notEqual':
return 'CAST(' + key + ' AS DECIMAL(10,2)) != ' + item.filter.toFixed(2);
case 'greaterThan':
return 'CAST(' + key + ' AS DECIMAL(10,2)) > ' + item.filter.toFixed(2);
case 'greaterThanOrEqual':
return 'CAST(' + key + ' AS DECIMAL(10,2)) >= ' + item.filter.toFixed(2);
case 'lessThan':
return 'CAST(' + key + ' AS DECIMAL(10,2)) < ' + item.filter.toFixed(2);
case 'lessThanOrEqual':
return 'CAST(' + key + ' AS DECIMAL(10,2)) <= ' + item.filter.toFixed(2);
case 'inRange':
return (
'(CAST(' +
key +
' AS DECIMAL(10,2)) >= ' +
item.filter +
' and CAST(' +
key +
' AS DECIMAL(10,2)) <= ' +
item.filterTo +
')'
);
default:
console.log('unknown number filter type: ' + item.type);
}
}
function dateFilterMapper(key, item) {
switch (item.type) {
case 'equals':
return key + ' = "' + moment(item.dateFrom).format('YYYY-MM-DD') + '"';
case 'notEqual':
return key + ' != "' + moment(item.dateFrom).format('YYYY-MM-DD') + '"';
case 'greaterThan':
return key + ' > "' + moment(item.dateFrom).format('YYYY-MM-DD') + '"';
case 'lessThan':
return key + ' < "' + moment(item.dateFrom).format('YYYY-MM-DD') + '"';
case 'inRange':
return (
'(' +
key +
' >= "' +
moment(item.dateFrom).format('YYYY-MM-DD') +
'" and ' +
key +
' <= "' +
moment(item.dateTo).format('YYYY-MM-DD') +
'")'
);
default:
console.log('unknown number filter type: ' + item.type);
}
}
function whereSql(request) {
var whereParts = [];
var filterModel = request.filterModel;
var rowGroups = request.rowGroupCols;
var groupKeys = request.groupKeys;
if (groupKeys) {
console.log('grouping where!');
groupKeys.forEach(function (key, i) {
console.log(key);
var value = typeof key === 'string' ? "'" + key + "'" : key;
whereParts.push(rowGroups[i].id + ' = ' + value);
});
}
if (filterModel) {
Object.keys(filterModel).forEach(function (columnKey) {
var filter = filterModel[columnKey];
switch (filter.filterType) {
case 'set':
whereParts.push(
columnKey + " IN ('" + filter.values.join("', '") + "')"
);
break;
case 'text':
whereParts.push(createFilterSql(textFilterMapper, columnKey, filter));
break;
case 'number':
whereParts.push(createFilterSql(numberFilterMapper, columnKey, filter));
break;
case 'date':
whereParts.push(createFilterSql(dateFilterMapper, columnKey, filter));
break;
default:
console.log('unknown filter type: ' + filter.filterType);
break;
}
});
}
var str = "(";
if (whereParts.length > 0) {
if (request.customParams.quickFilterValue !== '') {
if (columnDefs.length > 0) {
columnDefs.forEach(function (columnDef, index) {
if (columnDef.field !== 'no' && columnDef.field !== 'media' && columnDef.field !== 'media1') {
if (index === 0)
str += columnDef.field + " like '%" + request.customParams.quickFilterValue + "%";
else
str += "' OR " + columnDef.field + " like '%" + request.customParams.quickFilterValue + "%";
}
});
str +="')";
whereParts.push(str);
}
}
return ' WHERE ' + whereParts.join(' AND ');
}
else {
if (request.customParams.quickFilterValue !== '') {
columnDefs.forEach(function (columnDef, index) {
if (columnDef.field !== 'no' && columnDef.field !== 'media' && columnDef.field !== 'media1') {
if (index === 0)
str += columnDef.field + " like '%" + request.customParams.quickFilterValue + "%";
else
str += "' OR " + columnDef.field + " like '%" + request.customParams.quickFilterValue + "%";
}
});
str +="')";
return "WHERE " + str;
}
}
return '';
}
function groupBySql(request) {
var rowGroupCols = request.rowGroupCols;
var groupKeys = request.groupKeys;
if (isDoingGrouping(rowGroupCols, groupKeys)) {
var rowGroupCol = rowGroupCols[groupKeys.length];
return ' GROUP BY ' + rowGroupCol.id;
}
return '';
}
function orderBySql(request) {
var sortModel = request.sortModel;
if (sortModel.length === 0) return '';
var sorts = sortModel.map(function (s) {
return s.colId + ' ' + s.sort.toUpperCase();
});
return ' ORDER BY ' + sorts.join(', ');
}
function limitSql(request) {
var blockSize = request.endRow - request.startRow;
return ' LIMIT ' + (blockSize + 1) + ' OFFSET ' + request.startRow;
}
function getLastRowIndex(request, results) {
if (!results || results.length === 0) {
return request.startRow;
}
var currentLastRow = request.startRow + results.length;
return currentLastRow <= request.endRow ? currentLastRow : -1;
}
function interpolate(str, o) {
return str.replace(/{([^{}]*)}/g, function (a, b) {
var r = o[b];
return typeof r === 'string' || typeof r === 'number' ? r : a;
});
}
};
this.autoSizeAll = function autoSizeAll(gridOptions) {
var allColumnIds = [];
gridOptions.columnApi.getAllColumns().forEach((column) => {
allColumnIds.push(column.colId);
});
gridOptions.columnApi.autoSizeColumns(allColumnIds);
};

PART 3
Finally
BASIC USAGE
<div id="myGrid" class="ag-theme-balham" style="height: 100%;"></div>
<script type="text/javascript">
var columns = [
{headerName: '<?php echo ENTERPRISE_NAME; ?>', field: 'enterprise_name',
menuTabs: ['filterMenuTab', 'columnsMenuTab'],
lockPosition: true,
cellStyle: params => App.recordCellStyle(params),
pinned: 'left',
cellRenderer: App.CustomEnterpriseCellRenderer,
filter: 'agSetColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT enterprise_name FROM ? ORDER BY enterprise_name',
},
},
{headerName: '<?php echo SWATCH_CARD_CODE; ?>', field: 'code',
menuTabs: ['filterMenuTab', 'columnsMenuTab'],
lockPosition: true,
cellRenderer: 'agGroupCellRenderer',
cellStyle: params => App.recordCellStyle(params),
pinned: 'left',
filter: 'agSetColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT code FROM ? ORDER BY code',
},
},
{
headerName: '<?php echo TRANSACTIONS; ?>', field: 'transactions',
menuTabs: ['columnsMenuTab'],
lockPosition: true,
cellRenderer: function (event) {
var editUrl = '/swatch_cards/swatch_cards_edit/' + event.data.uid;
var editBtn = '<i class="fal fa-edit"></i>';
var orderUrl = '/swatch_cards/swatch_cards_orders_edit/' + event.data.uid;
var orderBtn = '<a href="#" onclick="App.loadModalFunc(\'' + orderUrl + '\',\'modal-sm\')" class="btn m-btn m-btn--hover-primary m-btn--icon m-btn--icon-only ' +
'm-portlet__nav-link"><i class="fa fa-shopping-cart"></i></a>';
var deleteBtn = '<i class="fal fa-trash-alt"></i>';
var printStickerUrl = '/swatch_cards/swatch_cards_print_sticker_modal/' + event.data.uid;
var printStickerBtn = '<i class="la la-qrcode"></i>';
return printStickerBtn + editBtn + orderBtn;
},
cellStyle: params => App.recordCellStyle(params),
pinned: 'left',
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.transactions)
},
{headerName: '<?php echo ENTERPRISE_SWATCH_CARD_CODE; ?>', field: 'enterprise_swatch_card_code', hide: true, filter: 'agTextColumnFilter'},
{headerName: '<?php echo ARTICLE_CODE; ?>', field: 'article_code', filter: 'agTextColumnFilter'},
{headerName: '<?php echo QUALITY_CODE; ?>', field: 'quality_code', filter: 'agTextColumnFilter'},
{headerName: '<?php echo CLOTH_TYPE; ?>', field: 'cloth_type',
cellRenderer: function(event) {
var cloth_type;
if(event.data.cloth_type == 0) {
cloth_type = 'Dokuma';
}
else if(event.data.cloth_type == 1) {
cloth_type = 'Örme';
}
else if(event.data.cloth_type == 2) {
cloth_type = 'Dantel-Brode';
}
else if(event.data.cloth_type == 3) {
cloth_type = 'PU-Deri';
}
return cloth_type;
},
valueFormatter: (data) => {
var cloth_type;
if(data.value == 0) {
cloth_type = 'Dokuma';
}
else if(data.value == 1) {
cloth_type = 'Örme';
}
else if(data.value == 2) {
cloth_type = 'Dantel-Brode';
}
else if(data.value == 3) {
cloth_type = 'PU-Deri';
}
return cloth_type;
},
filter: 'agSetColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT cloth_type FROM ? ORDER BY cloth_type',
valueFormatter: (data) => {
var cloth_type;
if(data.value == 0) {
cloth_type = 'Dokuma';
}
else if(data.value == 1) {
cloth_type = 'Örme';
}
else if(data.value == 2) {
cloth_type = 'Dantel-Brode';
}
else if(data.value == 3) {
cloth_type = 'PU-Deri';
}
return cloth_type;
},
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.cloth_type)
},
{headerName: '<?php echo WIDTH; ?>', field: 'width',
cellRenderer: (event) => {
return event.data.width + ' cm';
},
filter: 'agNumberColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT width FROM ? ORDER BY width',
suppressAndOrCondition: true,
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.width)
},
{headerName: '<?php echo WEIGHT; ?>', field: 'weight',
cellRenderer: (event) => {
var unit = '';
switch (event.data.weight_unit) {
case '3':
unit = 'Gr/m';
break;
case '4':
unit = 'Gr/m²';
break;
}
return event.data.weight + ' ' + unit;
},
filter: 'agNumberColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT weight FROM ? ORDER BY weight',
suppressAndOrCondition: true,
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.weight)
},
{headerName: '<?php echo QUANTITY; ?>', field: 'quantity',
cellRenderer: (event) => {
var unit = parseInt(event.data.unit);
var unit_text = '';
switch (unit) {
case 0:
unit_text = 'ad.';
break;
case 1:
unit_text = 'm';
break;
}
return event.data.quantity + ' ' + unit_text;
},
filter: 'agNumberColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT quantity FROM ? ORDER BY quantity',
totalRow: true,
suppressAndOrCondition: true,
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.quantity)
},
{headerName: '<?php echo PRICE; ?>', field: 'price',
cellRenderer: (event) => {
var currency = parseInt(event.data.currency),
currency_symbol = '',
price = (event.data.price) ? event.data.price : '0.00';
switch (currency) {
case 0:
currency_symbol = '₺';
break;
case 1:
currency_symbol = '€';
break;
case 2:
currency_symbol = '$';
break;
default:
currency_symbol = '₺';
break;
}
return currency_symbol + ' ' + price;
},
filter: 'agNumberColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT price FROM ? ORDER BY price',
suppressAndOrCondition: true,
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.price)
},
{headerName: '<?php echo DESCRIPTION; ?>', field: 'description', filter: 'agTextColumnFilter'},
{headerName: '<?php echo COMPOSITION; ?>', field: 'composition',
cellRenderer: (event) => {
var result='';
if (event.data.composition) {
var obj = JSON.parse(event.data.composition);
for (var item of obj) {
if(item.value > 0) {
result += item.key + ': ' + item.value + ', ';
}
}
result = result.slice(0,-2);
}
else {
result = '<div class="loader"></div>';
}
return result;
},
valueFormatter: (data) => {
var result='';
if (data.value) {
var obj = JSON.parse(data.value);
for (var item of obj) {
if(item.value > 0) {
result += item.key + ': ' + item.value + ', ';
}
}
result = result.slice(0,-2);
}
return result;
},
filter: 'agTextColumnFilter',
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.composition)
},
{headerName: '<?php echo CATEGORY; ?>', field: 'category',
cellRenderer: (event) => {
var result='';
if (event.data.category) {
var obj = JSON.parse(event.data.category);
for (var item of obj) {
if(item.is_checked) {
result += item.label + ', ';
}
}
result = result.slice(0,-2);
}
else {
result = '<div class="loader"></div>';
}
return result;
},
valueFormatter: (data) => {
var result='';
if (data.value) {
var obj = JSON.parse(data.value);
for (var item of obj) {
if(item.is_checked) {
result += item.label + ', ';
}
}
result = result.slice(0,-2);
}
return result;
},
filter: 'agTextColumnFilter',
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.category)
},
{headerName: '<?php echo XXX_MEDIA; ?>', field: 'media', hide: true, suppressColumnsToolPanel: true},
{headerName: '<?php echo YYY_MEDIA; ?>', field: 'media1', hide: true, suppressColumnsToolPanel: true},
{headerName: '<?php echo ENTERPRISE_MEDIA_COUNT; ?>', field: 'epc',
filter: 'agNumberColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT epc FROM ? ORDER BY epc',
totalRow: true,
suppressAndOrCondition: true,
}
},
{headerName: '<?php echo YYY_MEDIA_COUNT; ?>', field: 'tfpc',
filter: 'agNumberColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT tfpc FROM ? ORDER BY tfpc',
totalRow: true,
suppressAndOrCondition: true,
},
},
{
headerName: '<?php echo CREATED_DATE; ?>', field: 'created_date',
valueFormatter: function(data) {
return ((data.value !== null && moment(data.value, 'YYYY-MM-DD',true).isValid()) && (data.value !== '0000-00-00')) ? moment(data.value).format('DD.MM.YYYY') : '';
},
cellRenderer: function (event) {
var created_date = new Date(event.data.created_date);
return moment(created_date).format('DD.MM.YYYY');
},
filter: 'agDateColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT created_date FROM ? ORDER BY created_date',
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.created_date)
},
{
headerName: '<?php echo UPDATED_DATE; ?>', field: 'updated_date',
valueFormatter: function(data) {
return ((data.value !== null && moment(data.value, 'YYYY-MM-DD',true).isValid()) && (data.value !== '0000-00-00')) ? moment(data.value).format('DD.MM.YYYY') : '';
},
cellRenderer: function (event) {
var updated_date = new Date(event.data.updated_date);
return moment(updated_date).format('DD.MM.YYYY');
},
filter: 'agDateColumnFilter',
filterParams: {
fakeServerSql: 'SELECT DISTINCT updated_date FROM ? ORDER BY updated_date',
},
pinnedRowCellRenderer: event => App.recordPinnedRowCellRenderer(event.data.updated_date)
},
];
var detailColumns = [
{headerName: '<?php echo ENTERPRISE_MEDIA; ?>', field: 'media', width: 300, cellRenderer: XXX},
{headerName: '<?php echo YYY_MEDIA; ?>', field: 'media1', width: 300, cellRenderer: YYY},
];
var SSRMGridOptionsIncluded = {
suppressDragLeaveHidesColumns: true,
sideBar: {
toolPanels: [
{
id: 'columns',
labelDefault: 'Columns',
labelKey: 'columns',
iconKey: 'columns',
toolPanel: 'agColumnsToolPanel',
toolPanelParams: {
suppressRowGroups: true,
suppressValues: true,
suppressPivots: true,
suppressPivotMode: true,
},
},
],
},
masterDetail: true,
detailRowHeight: 500,
detailCellRendererParams: {
detailGridOptions: {
suppressDragLeaveHidesColumns: true,
defaultColDef: {
sortable: true,
filter: true,
resizable: true,
},
headerHeight: 45,
rowStyle: {fontFamily: 'Comic Sans MS, Comic Sans, Verdana', color: 'darkslategray'},
sideBar: {
hiddenByDefault: true,
toolPanels: [
{
id: 'columns',
labelDefault: 'Columns',
labelKey: 'columns',
iconKey: 'columns',
toolPanel: 'agColumnsToolPanel',
toolPanelParams: {
suppressRowGroups: true,
suppressValues: true,
suppressPivots: true,
suppressPivotMode: true,
},
},
],
defaultToolPanel: 'columns',
},
columnDefs: detailColumns,
rowHeight: 400,
groupUseEntireRow: true,
groupDefaultExpanded: -1,
animateRows: true,
localeText: AG_GRID_LANGUAGE_TEXT
},
getDetailRowData: function (params) {
console.log(params.data.media);
console.log(params.data.media1);
params.successCallback([{"media": params.data.media, "media1": params.data.media1}]);
setTimeout(() => {
var script = document.createElement('script');
script.type = 'text/javascript';
script.innerHTML = "$('.myZoom1').ezPlus({easing: true});$('.myZoom2').ezPlus({});";
document.body.appendChild(script);
}, 3000)
},
},
onGridReady: function (params) {
params.api.sizeColumnsToFit();
},
components: {
XXX: XXX,
YYY: YYY,
},
getRowStyle: function (event) {
if (event.data != null) {
if (event.data.uid === '<?php echo $this->session->userdata('COMPANY_UID'); ?>') {
return {backgroundColor: '#b8f2ff'};
}
}
return null;
}
};
<?php
$compositions_array = json_encode($compositions);
echo "var compositions_array = ". $compositions_array . ";\n";
?>
var dataList = {
filter_where: [
{
enterprise_uid: '<?php echo $filter_where['enterprise_uid']; ?>',
category: '<?php echo $filter_where['category']; ?>',
quality_code: '<?php echo $filter_where['quality_code']; ?>',
article_code: '<?php echo $filter_where['article_code']; ?>',
q_a_d: '<?php echo $filter_where['q_a_d']; ?>',
xxx_picture: '<?php echo $filter_where['xxx_picture']; ?>',
yyy_picture: '<?php echo $filter_where['yyy_picture']; ?>',
start_date: '<?php echo $filter_where['start_date']; ?>',
end_date: '<?php echo $filter_where['end_date']; ?>'
}
],
compositions: compositions_array,
csrf_test_name: csrfTokenHash
};
App.SSRMAgGrid(columns, SSRMGridOptionsIncluded, dataList, '/swatch_cards/swatch_cards_list_json', ['category', 'composition']);
</script>
Controller
public function swatch_cards_list_json() {
if(!Auth::isAllow(Db_auths_file::ListSwatchCards)) { $this->error_(); return; }
$data = array(
'company_uid' => $this->session->userdata('COMPANY_UID'),
'enterprise_uid' => $this->input->post('filter_where')[0]['enterprise_uid'],
'compositions' => (array)$this->input->post('compositions'),
'category' => $this->input->post('filter_where')[0]['category'],
'quality_code' => $this->input->post('filter_where')[0]['quality_code'],
'article_code' => $this->input->post('filter_where')[0]['article_code'],
'q_a_d' => $this->input->post('filter_where')[0]['q_a_d'],
'xxx_picture' => $this->input->post('filter_where')[0]['xxx_picture'],
'yyy_picture' => $this->input->post('filter_where')[0]['yyy_picture'],
'start_date' => $this->input->post('filter_where')[0]['start_date'],
'end_date' => $this->input->post('filter_where')[0]['end_date'],
'offset' => $this->input->post('offset'),
'limit' => $this->input->post('limit'),
);
$result['data'] = $this->swatch_cards_model->get_all_swatch_cards($data);
echo json_encode($result);
}
Model
public function get_all_swatch_cards($data) {
$dataArr = array(
'company_uid' => $data['company_uid'],
'start_date' => $data['start_date'],
'end_date' => $data['end_date']
);
$sql = "SELECT sc.id,sc.uid,sc.code,sc.enterprise_swatch_card_code,sc.cloth_type,sc.article_code,sc.quality_code,sc.width,sc.weight,sc.weight_unit,ifnull(s.quantity, 0) quantity,sc.unit,sc.price,currency,sc.description,"
."(SELECT m.path FROM medias m where m.swatch_card_uid = sc.uid AND m.media_owner = 0 LIMIT 1) media, "
."(SELECT m.path FROM medias m where m.swatch_card_uid = sc.uid AND m.media_owner = 1 LIMIT 1) media1, "
."DATE_FORMAT(sc.created_date, '%Y-%m-%d') created_date,DATE_FORMAT(sc.updated_date, '%Y-%m-%d') updated_date,e.id enterprise_id, e.uid enterprise_uid, e.name enterprise_name, SUM(CASE WHEN m.media_owner = 0 THEN 1 ELSE 0 END) epc, SUM(CASE WHEN m.media_owner = 1 THEN 1 ELSE 0 END) tfpc "
."FROM swatch_cards AS sc "
."LEFT JOIN "
."(SELECT sd.material_uid, IFNULL(SUM(IF(s.transaction_type=1, sd.quantity*-1, sd.quantity)), 0) quantity "
."FROM stocks s "
."LEFT JOIN stock_details sd ON (sd.company_uid = :company_uid AND s.uid = sd.stock_uid) "
."WHERE s.company_uid = :company_uid GROUP BY sd.material_uid) s ON (sc.uid = s.material_uid) "
."LEFT JOIN enterprises AS e ON(e.company_uid=:company_uid AND e.uid=sc.enterprise_uid) "
."LEFT JOIN medias m ON (sc.company_uid = m.company_uid AND sc.uid = m.swatch_card_uid) "
."WHERE sc.company_uid = :company_uid";
if($data['enterprise_uid'] != 0) {
$dataArr['enterprise_uid'] = $data['enterprise_uid'];
$sql .= " AND sc.enterprise_uid = :enterprise_uid";
}
if($data['category'] != '') {
$sql .= " AND sc.code like 'F-_____-____-%" . $data['category'] . "%'";
}
if($data['quality_code'] != '') {
$dataArr['quality_code'] = $data['quality_code'];
$sql .= " AND sc.quality_code like '%' :quality_code '%'";
}
if($data['article_code'] != '') {
$dataArr['article_code'] = $data['article_code'];
$sql .= " AND sc.article_code like '%' :article_code '%'";
}
if($data['q_a_d'] != '') {
$dataArr['q_a_d'] = $data['q_a_d'];
$sql .= " AND (sc.article_code like '%' :q_a_d '%' OR sc.quality_code like '%' :q_a_d '%' OR sc.description like '%' :q_a_d '%')";
}
if (count($data['compositions']) > 0) {
foreach ($data['compositions'] as $composition) {
if ($composition['value'] == 0) {
$var = "{\"key\":\"" . $composition['key'] . "\",\"value\":%}";
$var1 = "{\"key\":\"" . $composition['key'] . "\",\"value\":0}";
$sql .= " AND sc.composition like '%" . $var . "%' AND sc.composition not like '%" . $var1 . "%'";
}
else {
$var = "{\"key\":\"" . $composition['key'] . "\",\"value\":" . $composition['value'] . "}";
$sql .= " AND sc.composition like '%" . $var . "%'";
}
}
}
$sql .= " AND DATE(sc.created_date) BETWEEN :start_date AND :end_date";
$sql .= " GROUP BY sc.uid";
if($data['xxx_picture'] != '' || $data['yyy_picture'] != '') {
$sql .= " HAVING";
if ($data['xxx_picture'] != '' && $data['yyy_picture'] == '') {
if ($data['xxx_picture'] > 0) {
$sql .= " epc > 0";
}
else {
$sql .= " epc = 0";
}
}
else if ($data['xxx_picture'] == '' && $data['yyy_picture'] != '') {
if ($data['yyy_picture'] > 0) {
$sql .= " tfpc > 0";
}
else {
$sql .= " tfpc = 0";
}
}
else {
if ($data['xxx_picture'] > 0) {
$sql .= " epc > 0";
}
else {
$sql .= " epc = 0";
}
if ($data['yyy_picture'] > 0) {
$sql .= " AND tfpc > 0";
}
else {
$sql .= " AND tfpc = 0";
}
}
}
$sql .= " ORDER BY sc.created_date DESC";
return $this->db->mySelectAll($sql,$dataArr);
}

Related

How to properly define a CodeMirror language?

I'm trying to provide a basic autocompletion for something like this:
db.collection("Items").where("name", "==", "temp").limit(1).get();
Here's the code I have so far, using StreamLanguage of CodeMirror 6:
import {
IndentContext,
LanguageSupport,
StreamLanguage,
StringStream
} from "#codemirror/language";
import { tags as t } from "#lezer/highlight";
export const FireStoreLanguage = StreamLanguage.define({
name: "firestore",
startState: (indentUnit: number) => {
return {};
},
token: (stream: StringStream, state: any = {}): string | null => {
console.log(stream);
if (stream.match("db")) {
state.db = true;
return "keyword";
}
if (stream.match(".")) {
if (state.db) {
state.db = false;
state.collection = true;
return "keyword";
} else if (state.collection) {
state.collection = false;
state.where = true;
return "keyword";
} else if (state.where) {
state.where = false;
state.limit = true;
return "keyword";
} else if (state.limit) {
state.limit = false;
return "keyword";
}
}
if (stream.match("collection")) {
if (state.db) {
state.collection = true;
return "keyword";
}
}
if (stream.match("where")) {
if (state.collection) {
state.where = true;
return "keyword";
}
}
if (stream.match("limit")) {
if (state.where) {
state.limit = true;
return "keyword";
}
}
if (stream.match("get")) {
if (state.limit) {
state.limit = false;
return "keyword";
}
}
if (stream.match(/"(?:[^\\"]|\\.)*"/)) {
if (state.collection) {
return "string";
}
if (state.where) {
state.where = false;
state.whereValue = true;
return "string";
}
if (state.whereValue) {
state.whereValue = false;
return "string";
}
if (stream.match("==")) {
if (state.whereValue) {
state.whereValue = false;
state.whereOperator = true;
return "operator";
}
}
if (stream.match(/[0-9]+/)) {
if (state.limit) {
return "number";
}
}
}
stream.next();
return null;
},
blankLine: (state: {}, indentUnit: number): void => {},
copyState: (state: {}) => {},
indent: (
state: {},
textAfter: string,
context: IndentContext
): number | null => {
return 1;
},
languageData: {
commentTokens: { line: ";" },
},
tokenTable: {
db: t.keyword,
dot: t.punctuation,
collection: t.keyword,
get: t.keyword,
lParen: t.punctuation,
rParen: t.punctuation,
string: t.string,
},
});
export function firestore() {
return new LanguageSupport(FireStoreLanguage);
}
In React, here's how I use it(after building it):
import CodeMirror from "#uiw/react-codemirror";
import React from "react";
import { firestore } from "./firestore";
function App() {
const onChange = React.useCallback((value, viewUpdate) => {
console.log("value:", value);
}, []);
return (
<CodeMirror
value={``}
height="100vh"
extensions={[firestore()]}
onChange={onChange}
/>
);
}
export default App;
The editor loads okay, but no autocompletion is provided while I type!
What am I doing wrong or missing in the code above?
I was missing these parts:
export const FireStoreCompletion = FireStoreLanguage.data.of({
autocomplete: completeFromList([
{ label: "db", type: "namespace" },
{ label: "collection", type: "function" },
{ label: "where", type: "function" },
{ label: "limit", type: "function" },
{ label: "get", type: "function" },
]),
});
export function firestore() {
return new LanguageSupport(FireStoreLanguage, [FireStoreCompletion]);
}

Limiting agRichSelectCellEditor to n-items?

Is there any way to restrict the height (eg to n * .cellHeight) for the built-in agRichSelectCellEditor?
I see there is a cellRenderer but this only affects the individual list-items - I know I can always implement a custom editor but wanted to see if this was possible first
override the CSS rule .ag-rich-select-list
.ag-rich-select-list {
width: 100%;
min-width: 200px;
height: auto !important;
}
var students = [{
first_name: 'Bob',
last_name: 'Harrison',
gender: 'Male',
address: '1197 Thunder Wagon Common, Cataract, RI, 02987-1016, US, (401) 747-0763',
mood: "Happy",
country: {
name: 'Ireland',
code: 'IE'
}
}, {
first_name: 'Mary',
last_name: 'Wilson',
gender: 'Female',
age: 11,
address: '3685 Rocky Glade, Showtucket, NU, X1E-9I0, CA, (867) 371-4215',
mood: "Sad",
country: {
name: 'Ireland',
code: 'IE'
}
}, {
first_name: 'Sadiq',
last_name: 'Khan',
gender: 'Male',
age: 12,
address: '3235 High Forest, Glen Campbell, MS, 39035-6845, US, (601) 638-8186',
mood: "Happy",
country: {
name: 'Ireland',
code: 'IE'
}
}, {
first_name: 'Jerry',
last_name: 'Mane',
gender: 'Male',
age: 12,
address: '2234 Sleepy Pony Mall , Drain, DC, 20078-4243, US, (202) 948-3634',
mood: "Happy",
country: {
name: 'Ireland',
code: 'IE'
}
}];
// double the array twice, make more data!
students.forEach(function(item) {
students.push(cloneObject(item));
});
students.forEach(function(item) {
students.push(cloneObject(item));
});
students.forEach(function(item) {
students.push(cloneObject(item));
});
function cloneObject(obj) {
return JSON.parse(JSON.stringify(obj));
}
var columnDefs = [{
field: "first_name",
headerName: "First Name",
width: 120,
editable: true
},
{
field: "last_name",
headerName: "Last Name",
width: 120,
editable: true
},
{
field: "gender",
width: 100,
editable: true,
cellRenderer: 'genderCellRenderer',
cellEditor: 'agRichSelectCellEditor',
cellEditorParams: {
cellRenderer: 'genderCellRenderer',
values: ['Male', 'Female']
}
},
{
field: "age",
width: 80,
editable: true,
cellEditor: 'numericCellEditor'
},
{
field: "mood",
width: 100,
cellRenderer: 'moodCellRenderer',
cellEditor: 'moodEditor',
editable: true
},
{
field: "country",
width: 110,
cellRenderer: 'countryCellRenderer',
cellEditor: 'agRichSelectCellEditor',
keyCreator: function(country) {
return country.name;
},
cellEditorParams: {
cellRenderer: 'countryCellRenderer',
values: [{
name: 'Ireland',
code: 'IE'
},
{
name: 'UK',
code: 'UK'
},
{
name: 'France',
code: 'FR'
}
]
},
editable: true
},
{
field: "address",
editable: true,
cellEditor: 'agLargeTextCellEditor',
cellEditorParams: {
maxLength: '300', // override the editor defaults
cols: '50',
rows: '6'
}
}
];
var gridOptions = {
columnDefs: columnDefs,
rowData: students,
defaultColDef: {
editable: true,
sortable: true,
flex: 1,
minWidth: 100,
filter: true,
resizable: true
},
onRowEditingStarted: function(event) {
console.log('never called - not doing row editing');
},
onRowEditingStopped: function(event) {
console.log('never called - not doing row editing');
},
onCellEditingStarted: function(event) {
console.log('cellEditingStarted');
},
onCellEditingStopped: function(event) {
console.log('cellEditingStopped');
},
components: {
genderCellRenderer: GenderCellRenderer,
numericCellEditor: NumericCellEditor,
moodCellRenderer: MoodCellRenderer,
moodEditor: MoodEditor,
countryCellRenderer: CountryCellRenderer
}
};
function getCharCodeFromEvent(event) {
event = event || window.event;
return (typeof event.which == "undefined") ? event.keyCode : event.which;
}
function isCharNumeric(charStr) {
return !!/\d/.test(charStr);
}
function isKeyPressedNumeric(event) {
var charCode = getCharCodeFromEvent(event);
var charStr = String.fromCharCode(charCode);
return isCharNumeric(charStr);
}
// simple function cellRenderer, just returns back the name of the country
function CountryCellRenderer(params) {
return params.value.name;
}
// function to act as a class
function NumericCellEditor() {}
// gets called once before the renderer is used
NumericCellEditor.prototype.init = function(params) {
// create the cell
this.eInput = document.createElement('input');
if (isCharNumeric(params.charPress)) {
this.eInput.value = params.charPress;
} else {
if (params.value !== undefined && params.value !== null) {
this.eInput.value = params.value;
}
}
var that = this;
this.eInput.addEventListener('keypress', function(event) {
if (!isKeyPressedNumeric(event)) {
that.eInput.focus();
if (event.preventDefault) event.preventDefault();
} else if (that.isKeyPressedNavigation(event)) {
event.stopPropagation();
}
});
// only start edit if key pressed is a number, not a letter
var charPressIsNotANumber = params.charPress && ('1234567890'.indexOf(params.charPress) < 0);
this.cancelBeforeStart = charPressIsNotANumber;
};
NumericCellEditor.prototype.isKeyPressedNavigation = function(event) {
return event.keyCode === 39 ||
event.keyCode === 37;
};
// gets called once when grid ready to insert the element
NumericCellEditor.prototype.getGui = function() {
return this.eInput;
};
// focus and select can be done after the gui is attached
NumericCellEditor.prototype.afterGuiAttached = function() {
this.eInput.focus();
};
// returns the new value after editing
NumericCellEditor.prototype.isCancelBeforeStart = function() {
return this.cancelBeforeStart;
};
// example - will reject the number if it contains the value 007
// - not very practical, but demonstrates the method.
NumericCellEditor.prototype.isCancelAfterEnd = function() {
var value = this.getValue();
return value.indexOf('007') >= 0;
};
// returns the new value after editing
NumericCellEditor.prototype.getValue = function() {
return this.eInput.value;
};
// any cleanup we need to be done here
NumericCellEditor.prototype.destroy = function() {
// but this example is simple, no cleanup, we could even leave this method out as it's optional
};
// if true, then this editor will appear in a popup
NumericCellEditor.prototype.isPopup = function() {
// and we could leave this method out also, false is the default
return false;
};
function GenderCellRenderer() {}
GenderCellRenderer.prototype.init = function(params) {
this.eGui = document.createElement('span');
if (params.value !== "" || params.value !== undefined || params.value !== null) {
var gender = '<img border="0" width="15" height="10" src="https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/images/' + params.value.toLowerCase() + '.png">';
this.eGui.innerHTML = gender + ' ' + params.value;
}
};
GenderCellRenderer.prototype.getGui = function() {
return this.eGui;
};
function MoodCellRenderer() {}
MoodCellRenderer.prototype.init = function(params) {
this.eGui = document.createElement('span');
if (params.value !== "" || params.value !== undefined || params.value !== null) {
var imgForMood = params.value === 'Happy' ? 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/images/smiley.png' : 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/images/smiley-sad.png';
this.eGui.innerHTML = '<img width="20px" src="' + imgForMood + '" />';
}
};
MoodCellRenderer.prototype.getGui = function() {
return this.eGui;
};
function MoodEditor() {
this.defaultImgStyle = 'padding-left:10px; padding-right:10px; border: 1px solid transparent; padding: 4px;';
this.selectedImgStyle = 'padding-left:10px; padding-right:10px; border: 1px solid lightgreen; padding: 4px;';
}
MoodEditor.prototype.onKeyDown = function(event) {
var key = event.which || event.keyCode;
if (key == 37 || // left
key == 39) { // right
this.toggleMood();
event.stopPropagation();
}
};
MoodEditor.prototype.toggleMood = function() {
this.selectMood(this.mood === 'Happy' ? 'Sad' : 'Happy');
};
MoodEditor.prototype.init = function(params) {
this.container = document.createElement('div');
this.container.style = "border-radius: 15px; border: 1px solid grey;background: #e6e6e6;padding: 15px; text-align:center;display:inline-block;outline:none";
this.container.tabIndex = "0"; // to allow the div to capture keypresses
this.happyImg = document.createElement('img');
this.happyImg.src = 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/images/smiley.png';
this.happyImg.style = this.defaultImgStyle;
this.sadImg = document.createElement('img');
this.sadImg.src = 'https://raw.githubusercontent.com/ag-grid/ag-grid/master/grid-packages/ag-grid-docs/src/images/smiley-sad.png';
this.sadImg.style = this.defaultImgStyle;
this.container.appendChild(this.happyImg);
this.container.appendChild(this.sadImg);
var that = this;
this.happyImg.addEventListener('click', function(event) {
that.selectMood('Happy');
params.stopEditing();
});
this.sadImg.addEventListener('click', function(event) {
that.selectMood('Sad');
params.stopEditing();
});
this.container.addEventListener('keydown', function(event) {
that.onKeyDown(event)
});
this.selectMood(params.value);
};
MoodEditor.prototype.selectMood = function(mood) {
this.mood = mood;
this.happyImg.style = (mood === 'Happy') ? this.selectedImgStyle : this.defaultImgStyle;
this.sadImg.style = (mood === 'Sad') ? this.selectedImgStyle : this.defaultImgStyle;
};
// gets called once when grid ready to insert the element
MoodEditor.prototype.getGui = function() {
return this.container;
};
MoodEditor.prototype.afterGuiAttached = function() {
this.container.focus();
};
MoodEditor.prototype.getValue = function() {
return this.mood;
};
// any cleanup we need to be done here
MoodEditor.prototype.destroy = function() {};
MoodEditor.prototype.isPopup = function() {
return true;
};
// setup the grid after the page has finished loading
document.addEventListener('DOMContentLoaded', function() {
var gridDiv = document.querySelector('#myGrid');
new agGrid.Grid(gridDiv, gridOptions);
});
.ag-rich-select-list {
width: 100%;
min-width: 200px;
height: auto !important;
}
<!DOCTYPE html>
<html lang="en">
<head>
<script>
var __basePath = './';
</script>
<style media="only screen">
html,
body {
height: 100%;
width: 100%;
margin: 0;
box-sizing: border-box;
-webkit-overflow-scrolling: touch;
}
html {
position: absolute;
top: 0;
left: 0;
padding: 0;
overflow: auto;
}
body {
padding: 1rem;
overflow: auto;
}
</style>
<script src="https://unpkg.com/#ag-grid-enterprise/all-modules#24.1.0/dist/ag-grid-enterprise.min.js"></script>
</head>
<body>
<div id="myGrid" style="height: 100%;" class="ag-theme-alpine"></div>
</body>
</html>

issue with creating multiple leaflet layers from one geojson file

I have included my code below. While it is sort of working, ill get to that in a minute, I feel like there is a better, more efficient, more correct, way to achieve my goal. I have a map for different weather options, in this case, Tornado, Severe Weather and Flash flood warnings. These are all included in one geojson file. The geo json file has a property called LayerId. This determines the time in the loop that the layer would show. I have a simple global map loop that constantly runs from 0 - 11. So if I am on loop 5, then only the data that corresponds with LayerId 5 would be visible. All others would be hidden/removed (which ever is preferred). When the loop hits 6, the layer corresponding to LayerId 5 would go away and LayerId 6 would now show and so on. Once the loop reaches 11, it starts over at 0.
I am not using a leaflet control due to the site requirements so i am using my own simple check box controls. when the check box is clicked, it calls a toggleLayer function to apply filters to my data. If FlashFlood is checked then only the data corresponding to the flash flood would show over the course of the loop IF it has data for flash flood at that interval.
When i said that it is sort of working...in my loop function i have a call to remove a layer. this works except every now and then it throws a null or undefined error. problem is is that its never the same layer. each time i start the application, its a different layer that errors out.
Below i have included a sample of my geojson and the code. The entry point for the code is at the toggleLayer function.
Thanks for any and all help.
GEOJSON FILE
{
"name": "WarningsJson",
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"coordinates": [
[
[ -86.00, 31.00 ],
[ -86.00, 32.00 ],
[ -87.00, 30.00 ],
[ -86.00, 31.00 ]
]
],
"type": "Polygon"
},
"properties": {
"type": null,
"strokeColor": "#ff9aa3",
"StartDateTime": "09/29/2020 7:30:00 AM",
"EndDateTime": "09/29/2020 9:30:00 AM",
"strokeThickness": 20,
"InfoboxTitle": "SFFW",
"Station": "KMOB",
"Identifier": "FFW",
"LayerId": "0"
}
},
{
"type": "Feature",
"geometry": {
"coordinates": [
[
[ -87.00, 32.00 ],
[ -87.00, 33.00 ],
[ -88.00, 31.00 ],
[ -87.00, 32.00 ]
]
],
"type": "Polygon"
},
"properties": {
"type": null,
"strokeColor": "#c0ffd4",
"StartDateTime": "09/29/2020 7:30:00 AM",
"EndDateTime": "09/29/2020 9:30:00 AM",
"strokeThickness": 2,
"InfoboxTitle": "TOR",
"Station": "KMOB",
"Identifier": "TOR",
"LayerId": "1"
}
},......
APPLICATION CODE
var WarnStormModel = (function () {
var layer0 = new L.LayerGroup();
var layer1 = new L.LayerGroup();
var layer2 = new L.LayerGroup();
var layer3 = new L.LayerGroup();
var layer4 = new L.LayerGroup();
var layer5 = new L.LayerGroup();
var layer6 = new L.LayerGroup();
var layer7 = new L.LayerGroup();
var layer8 = new L.LayerGroup();
var layer9 = new L.LayerGroup();
var layer10 = new L.LayerGroup();
var layer11 = new L.LayerGroup();
var warnConditionsLayersGroup = [layer0, layer1, layer2, layer3, layer4, layer5, layer6, layer7, layer8, layer9, layer10, layer11];
var tornadoActive = false;
var svrActive = false;
var ffwActive = false;
const WarnFilter = {
tornado: null,
svr: null,
flood: null
}
function init() {
$.getJSON('/Data/GeoJsonFiles/WarningJsons/Warnings_0.json', function (data) {
L.geoJSON(data, {
style: function (feature) {
return {
color: feature.properties.strokeColor,
fillOpacity: 0
};
},
pane: "warnPane",
onEachFeature: function (feature, layer) {
var popupText = '<div>'
+ '<span style="float: right; cursor: pointer; cursor: hand"</i></span><br>'
+ '<b>LAYER: </b>' + layer.feature.properties.LayerId + '<br>'
+ '<b>TYPE: </b>' + layer.feature.properties.InfoboxTitle + '<br>'
+ '<b>STATION:</b>' + layer.feature.properties.Station + '<br>'
+ '<b>START: </b>' + layer.feature.properties.StartDateTime + '<br>'
+ '<b>END: </b>' + layer.feature.properties.EndDateTime + '<br>';
layer.bindPopup(popupText);
layer._leaflet_id = feature.properties.LayerId;
if (feature.properties.LayerId == "0") { layer0.addLayer(layer); }
else if (feature.properties.LayerId == "1") { layer1.addLayer(layer); }
else if (feature.properties.LayerId == "2") { layer2.addLayer(layer); }
else if (feature.properties.LayerId == "3") { layer3.addLayer(layer); }
else if (feature.properties.LayerId == "4") { layer4.addLayer(layer); }
else if (feature.properties.LayerId == "5") { layer5.addLayer(layer); }
else if (feature.properties.LayerId == "6") { layer6.addLayer(layer); }
else if (feature.properties.LayerId == "7") { layer7.addLayer(layer); }
else if (feature.properties.LayerId == "8") { layer8.addLayer(layer); }
else if (feature.properties.LayerId == "9") { layer9.addLayer(layer); }
else if (feature.properties.LayerId == "10") { layer10.addLayer(layer); }
else if (feature.properties.LayerId == "11") { layer11.addLayer(layer); }
},
filter: function (feature, layer) {
return (
feature.properties.Identifier === WarnFilter.tornado ||
feature.properties.Identifier === WarnFilter.svr ||
feature.properties.Identifier === WarnFilter.flood
)
},
interactive: true
});
}).fail(function (err) { console.log('createWarningsErr: ', err); })
};
//**********//
function isActive(layer) {
if (layer == "TOR") { return tornadoActive; }
else if (layer == "SVR") { return tstrmActive; }
else if (layer == "FFW") { return ffwActive; }
}
var isAnyActive = function () { return tornadoActive || svrActive || ffwActive; }
var toggleLayer = function (layer, checkState) {
switch (layer) {
case "TOR": (checkState) ? WarnFilter.tornado = 'TOR' : WarnFilter.tornado = null; tornadoActive = !tornadoActive;
break;
case "SVR": (checkState) ? WarnFilter.svr = 'SVR' : WarnFilter.svr = null; svrActive = !svrActive;
break;
case "FFW": (checkState) ? WarnFilter.flood = 'FFW' : WarnFilter.flood = null; ffwActive = !ffwActive;
break;
default:
if (checkState) {
for (key in WarnFilter) {
if (WarnFilter.hasOwnProperty(key)) {
debugger
WarnFilter[key] = (key.toString()).toUpperCase();
}
}
}
//set all values in filter themselves to show
else {
for (key in WarnFilter) {
if (WarnFilter.hasOwnProperty(key)) {
WarnFilter[key] = null;
}
}
}
break;
}
showHide(layer, checkState);
}
//**********//
var showHide = function (layer, checkState) {
rerender();
if (isAnyActive() && checkState) {
warnConditionsLayersGroup[GlobalMapLoop.getLoopIndex()].addTo(getMap());
}
else {
warnConditionsLayersGroup[GlobalMapLoop.getLoopIndex()].removeLayer(getMap());
}
}
var loop = function (currentIndex, pastIndex) {
console.log("got to warn loop", currentIndex, pastIndex, isAnyActive())
if (isAnyActive()) {
getMap().removeLayer(warnConditionsLayersGroup[pastIndex]);
getMap().addLayer(warnConditionsLayersGroup[currentIndex]);
}
}
var rerender = (function () {
init();
})
return {
init: init,
toggleLayer: toggleLayer,
loop: loop,
rerender: rerender
};
})();

Can yadcf be used to filter rows on a class

Im looking for an easy way to filter 3 cols which certain rows have a specific class.
I have a datatable for live football games, this shows me inplay stats and if stats get above a certain level I add a class to the TD, for example
if(Number(chars[0])+Number(chars[1])>15)
{
$(row).find('td:eq(8)').css('background-color', 'lime');
$(row).find('td:eq(8)').addClass('lime');
}
On a busy day I can have over 100 live games at a time and I would like to filter just showing the games which have a class of 'lime'. There are 3 cols I shade using this so ideally I want a single filter to cover all 3
Ive tried using the search plugin for datatables but I can only get it to work on the first page of results.
const table = $('#example').DataTable( {
responsive: {
details: {
}
},
"ajax": "console.json",
"search": {
"regex": true
},
"columns": [
{
"class": 'details-control',
"orderable": false,
"data": null,
"defaultContent": ''
},
{ "data": "time" },
{ "data": "firstgoal" },
{ "data": "score" },
{ "data": "league" },
{ "data": "teams", "defaultContent": "0" },
{ "data": function (data, type, dataToSet) {
return data.nextover + "% (" + data.sno + ")";}
},
{ "data": "pi1",
"render": function (data, type, row) {
return '<span class="show-modalp1" data-modal-id="'+ row.gameid + '">' + data + '</span>';
}},
{ "data": "pi2",
"render": function (data, type, row) {
return '<span class="show-modalp2" data-modal-id="'+ row.gameid + '">' + data + '</span>';
}},
{ "render": function (data, type, JsonResultRow, meta) {
return '<img src="'+JsonResultRow.rc+'" class="center">';
}}
],
"order": [[1, 'asc']],
rowCallback: function(row, data, index){
if((data["nextover"] >85 && data["sno"]>14) || (data["nextover"] >70 && data["sno"]>10 && data["time"]>=70) || (data["nextover"] >55 && data["sno"]>5 && data["time"]>=81))
{
$(row).find('td:eq(6)').css('background-color', 'lime');
$(row).find('td:eq(7)').addClass('lime');
}
else
{
$(row).find('td:eq(6)').css('background-color', 'mistyrose');
}
var chars = data["pi2"].split(':');
if(Number(chars[0])+Number(chars[1])>15)
{
$(row).find('td:eq(8)').css('background-color', 'lime');
$(row).find('td:eq(8)').addClass('lime');
}
else
{
$(row).find('td:eq(8)').css('background-color', 'mistyrose');
}
var chars2 = data["pi1"].split(':');
if(Number(chars2[0])+Number(chars2[1])>70)
{
$(row).find('td:eq(7)').css('background-color', 'lime');
$(row).find('td:eq(7)').addClass('lime');
}
else
{
$(row).find('td:eq(7)').css('background-color', 'mistyrose');
}
},
drawCallback: function(settings) {
$(".show-modalp1").each(function() {
var id = $(this).attr('data-modal-id');
$(this).popover({
content: function() {
return '<div style="width: 440px; height=220px"><iframe src="../pressure/showgraph.php?gameid=' + id + '&pi=pi1" width=440 height=220></iframe></div>';
},
trigger: 'hover',
placement: 'left',
html: true,
title: 'Pressure Index 1 = (10*SOT)+(10*SOFT)+1 per 5%poss'
}
)
});
$(".show-modalp2").each(function() {
var id = $(this).attr('data-modal-id');
$(this).popover({
content: function() {
return '<div style="width: 440px; height=220px"><iframe src="../pressure/showgraph.php?gameid=' + id + '&pi=pi2" width=440 height=220></iframe></div>';
},
trigger: 'hover',
placement: 'left',
html: true,
title: 'Pressure Index 2 = 1 *dangerous attack - 1 point if no corners in last 8'
}
)
});
},
} );
$.fn.DataTable.ext.search.push(function(_,__,rowIdx){
return $(table.row(rowIdx).node()).has('td.lime').length || !$('#showLime').hasClass('limeOnly');
});
$('#example').on('draw.dt', function () {
$('[data-toggle="tooltip"]').tooltip();
});
// Add event listener for opening and closing details
$('#example tbody').on('click', 'td.details-control', function () {
var tr = $(this).parents('tr');
var row = table.row( tr );
if ( row.child.isShown() ) {
// This row is already open - close it
row.child.hide();
tr.removeClass('shown');
}
else {
// Open this row
row.child( format(row.data()) ).show();
tr.addClass('shown');
}
} );
$('#reset').on('click', function () {
table.columns(1).search("").draw();
table.ajax.reload(null, false);
})
$('#secondhalf').on('click', function () {
regExSearch = '(4[6-9]|[5-8][0-9]|90)';
table.column(1).search(regExSearch, true, false).draw();
});
$('#firsthalf').on('click', function () {
regExSearch = '([1-3][0-9]|4[0-5])';
table.column(1).search(regExSearch, true, false).draw();
});
$('#halftime').on('click', function () {
table.columns(1).search("HT").draw();
});
;
$('#showLime').on('change', function () {
$(this).toggleClass('limeOnly');
table.draw();
});
setInterval( function () {
table.ajax.reload( null, false ); // user paging is not reset on reload
}, 30000 )
}
);

Creating Mongo/mapReduce data source in icCube

I would like to create a MongoDB/mapReduce data source into icCube (http://www.iccube.com/support/documentation/user_guide/schemas_cubes/ds_mongodb.php#mapReduce), The below script works fine from Mongo shell, how it should be formatted to be accepted by icCube, when I paste the same code into icCube datasource builder, I get this error message:
MongoDB : invalid JSON (table:Test.mapReduce) : [var location_map = function() { if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) { emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0}); } } var country_map = function() { if (this.companyId = "1234") { emit(this._id, { CountryName: this.CountryName, Location_NameOfLocation: "", LocationID: 0, companyId : this.companyId }); } } var r = function(key, values) { var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0}; values.forEach(function(value) { if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; } if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; } if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; } if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; } }); return result; } db.runCommand( { mapReduce: Location, map: location_map, reduce: r, out: { replace: LocationsCountries }, query: {companyId : "1234"} } ) db.runCommand( { mapReduce: Countries, map: country_map, reduce: r, out: { reduce: LocationsCountries }, query: {companyId : "1234" } } )] ^
Mongo mapReduce script:
var location_map = function() {
if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) {
emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0});
}
}
var country_map = function() {
if (this.companyId = "1234") {
emit(this._id, { CountryName: this.CountryName, Location_NameOfLocation: "", LocationID: 0, companyId : this.companyId });
}
}
var r = function(key, values) {
var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0};
values.forEach(function(value) {
if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; }
if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; }
if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; }
if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; }
});
return result;
}
db.runCommand(
{
mapReduce: "Location",
map: location_map,
reduce: r,
out: { replace: "LocationsCountries" },
query: {companyId : "1234"}
}
)
db.runCommand(
{
mapReduce: "Countries",
map: country_map,
reduce: r,
out: { reduce: "LocationsCountries" },
query: {companyId : "1234" }
}
)
Thanks,
Balint
You'll have to define your functions in strings ( and Javascript escape if required strings within these strings ). This is described here in the icCube documentation ( www ). Here is an example combining single quoted strings with double-quoted strings.
{
"mapReduce": "Location",
"map": 'function() {
if (this.companyId = "1234" && this.Parent_Location !== undefined && this.Parent_Location.value.length > 0 && this.Parent_Location.value[0].id !== undefined && this.Parent_Location.value[0].id !== null) {
emit(this.Parent_Location.value[0].id, {Location_NameOfLocation: this.Location_NameOfLocation, LocationID: this._id, CountryName: "", companyId : 0});
}
}',
"reduce": 'function(key, values) {
var result = {LocationID: 0, CountryName: "", Location_NameOfLocation: "", companyId : 0};
values.forEach(function(value) {
if (result.LocationID === 0 && value.LocationID !== null ) { result.LocationID = value.LocationID; }
if (result.companyId === 0 && value.companyId !== null ) { result.companyId = value.companyId; }
if (result.CountryName === "" && value.CountryName !== null ) { result.CountryName = value.CountryName; }
if (result.Location_NameOfLocation === "" && value.Location_NameOfLocation !== null ) { result.Location_NameOfLocation = value.Location_NameOfLocation; }
});
return result;
}',
"out": {
"replace": "LocationsCountries"
},
"query": {
"companyId" : "1234"
}
}