ng-grid
Version:
__Contributors:__
237 lines (228 loc) • 9.64 kB
JavaScript
ng.RowFactory = function (grid, $scope, domUtilityService, $templateCache) {
var self = this;
// we cache rows when they are built, and then blow the cache away when sorting
self.aggCache = {};
self.parentCache = []; // Used for grouping and is cleared each time groups are calulated.
self.dataChanged = true;
self.parsedData = [];
self.rowConfig = {};
self.selectionService = $scope.selectionService;
self.rowHeight = 30;
self.numberOfAggregates = 0;
self.groupedData = undefined;
self.rowHeight = grid.config.rowHeight;
self.rowConfig = {
enableRowSelection: grid.config.enableRowSelection,
rowClasses: grid.config.rowClasses,
selectedItems: $scope.selectedItems,
selectWithCheckboxOnly: grid.config.selectWithCheckboxOnly,
beforeSelectionChangeCallback: grid.config.beforeSelectionChange,
afterSelectionChangeCallback: grid.config.afterSelectionChange,
jqueryUITheme: grid.config.jqueryUITheme,
enableCellSelection: grid.config.enableCellSelection,
rowHeight: grid.config.rowHeight
};
self.renderedRange = new ng.Range(0, grid.minRowsToRender() + EXCESS_ROWS);
// @entity - the data item
// @rowIndex - the index of the row
self.buildEntityRow = function(entity, rowIndex) {
// build the row
return new ng.Row(entity, self.rowConfig, self.selectionService, rowIndex);
};
self.buildAggregateRow = function(aggEntity, rowIndex) {
var agg = self.aggCache[aggEntity.aggIndex]; // first check to see if we've already built it
if (!agg) {
// build the row
agg = new ng.Aggregate(aggEntity, self, self.rowConfig.rowHeight);
self.aggCache[aggEntity.aggIndex] = agg;
}
agg.rowIndex = rowIndex;
agg.offsetTop = rowIndex * self.rowConfig.rowHeight;
return agg;
};
self.UpdateViewableRange = function(newRange) {
self.renderedRange = newRange;
self.renderedChange();
};
self.filteredRowsChanged = function() {
// check for latebound autogenerated columns
if (grid.lateBoundColumns && grid.filteredRows.length > 0) {
grid.config.columnDefs = undefined;
grid.buildColumns();
grid.lateBoundColumns = false;
$scope.$evalAsync(function() {
$scope.adjustScrollLeft(0);
});
}
self.dataChanged = true;
if (grid.config.groups.length > 0) {
self.getGrouping(grid.config.groups);
}
self.UpdateViewableRange(self.renderedRange);
};
self.renderedChange = function() {
if (!self.groupedData || grid.config.groups.length < 1) {
self.renderedChangeNoGroups();
grid.refreshDomSizes();
return;
}
self.wasGrouped = true;
self.parentCache = [];
var x = 0;
var temp = self.parsedData.filter(function (e) {
if (e.isAggRow) {
if (e.parent && e.parent.collapsed) {
return false;
}
return true;
}
if (!e[NG_HIDDEN]) {
e.rowIndex = x++;
}
return !e[NG_HIDDEN];
});
self.totalRows = temp.length;
var rowArr = [];
for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) {
if (temp[i]) {
temp[i].offsetTop = i * grid.config.rowHeight;
rowArr.push(temp[i]);
}
}
grid.setRenderedRows(rowArr);
};
self.renderedChangeNoGroups = function () {
var rowArr = [];
for (var i = self.renderedRange.topRow; i < self.renderedRange.bottomRow; i++) {
if (grid.filteredRows[i]) {
grid.filteredRows[i].rowIndex = i;
grid.filteredRows[i].offsetTop = i * grid.config.rowHeight;
rowArr.push(grid.filteredRows[i]);
}
}
grid.setRenderedRows(rowArr);
};
self.fixRowCache = function () {
var newLen = grid.data.length;
var diff = newLen - grid.rowCache.length;
if (diff < 0) {
grid.rowCache.length = grid.rowMap.length = newLen;
} else {
for (var i = grid.rowCache.length; i < newLen; i++) {
grid.rowCache[i] = grid.rowFactory.buildEntityRow(grid.data[i], i);
}
}
};
//magical recursion. it works. I swear it. I figured it out in the shower one day.
self.parseGroupData = function(g) {
if (g.values) {
for (var x = 0; x < g.values.length; x++){
// get the last parent in the array because that's where our children want to be
self.parentCache[self.parentCache.length - 1].children.push(g.values[x]);
//add the row to our return array
self.parsedData.push(g.values[x]);
}
} else {
for (var prop in g) {
// exclude the meta properties.
if (prop == NG_FIELD || prop == NG_DEPTH || prop == NG_COLUMN) {
continue;
} else if (g.hasOwnProperty(prop)) {
//build the aggregate row
var agg = self.buildAggregateRow({
gField: g[NG_FIELD],
gLabel: prop,
gDepth: g[NG_DEPTH],
isAggRow: true,
'_ng_hidden_': false,
children: [],
aggChildren: [],
aggIndex: self.numberOfAggregates,
aggLabelFilter: g[NG_COLUMN].aggLabelFilter
}, 0);
self.numberOfAggregates++;
//set the aggregate parent to the parent in the array that is one less deep.
agg.parent = self.parentCache[agg.depth - 1];
// if we have a parent, set the parent to not be collapsed and append the current agg to its children
if (agg.parent) {
agg.parent.collapsed = false;
agg.parent.aggChildren.push(agg);
}
// add the aggregate row to the parsed data.
self.parsedData.push(agg);
// the current aggregate now the parent of the current depth
self.parentCache[agg.depth] = agg;
// dig deeper for more aggregates or children.
self.parseGroupData(g[prop]);
}
}
}
};
//Shuffle the data into their respective groupings.
self.getGrouping = function(groups) {
self.aggCache = [];
self.numberOfAggregates = 0;
self.groupedData = {};
// Here we set the onmousedown event handler to the header container.
var rows = grid.filteredRows,
maxDepth = groups.length,
cols = $scope.columns;
for (var x = 0; x < rows.length; x++){
var model = rows[x].entity;
if (!model) return;
rows[x][NG_HIDDEN] = true;
var ptr = self.groupedData;
for (var y = 0; y < groups.length; y++) {
var group = groups[y];
var col = cols.filter(function(c) {
return c.field == group;
})[0];
var val = ng.utils.evalProperty(model, group);
val = val ? val.toString() : 'null';
if (!ptr[val]) {
ptr[val] = {};
}
if (!ptr[NG_FIELD]) {
ptr[NG_FIELD] = group;
}
if (!ptr[NG_DEPTH]) {
ptr[NG_DEPTH] = y;
}
if (!ptr[NG_COLUMN]) {
ptr[NG_COLUMN] = col;
}
ptr = ptr[val];
}
if (!ptr.values) {
ptr.values = [];
}
ptr.values.push(rows[x]);
};
//moved out of above loops due to if no data initially, but has initial grouping, columns won't be added
for (var z = 0; z < groups.length; z++) {
if (!cols[z].isAggCol && z <= maxDepth) {
cols.splice(0, 0, new ng.Column({
colDef: {
field: '',
width: 25,
sortable: false,
resizable: false,
headerCellTemplate: '<div class="ngAggHeader"></div>',
pinned: grid.config.pinSelectionCheckbox
},
isAggCol: true,
headerRowHeight: grid.config.headerRowHeight
}, $scope, grid, domUtilityService, $templateCache));
}
}
domUtilityService.BuildStyles($scope, grid, true);
grid.fixColumnIndexes();
$scope.adjustScrollLeft(0);
self.parsedData.length = 0;
self.parseGroupData(self.groupedData);
self.fixRowCache();
};
if (grid.config.groups.length > 0 && grid.filteredRows.length > 0) {
self.getGrouping(grid.config.groups);
}
};