<!DOCTYPE html>
<html>
<head>
<script data-require="angular.js@1.3.0-rc2" data-semver="1.3.0-rc2" src="https://code.angularjs.org/1.3.0-rc.2/angular.js"></script>
<link rel="stylesheet" href="style.css" />
<script src="app.js"></script>
<script src="appController.js"></script>
<script src="gridManager.js"></script>
<script src="baseGrid.js"></script>
<script src="grid.js"></script>
</head>
<body ng-app="GridApp" ng-controller="AppController">
<div wm-data-grid grid-options="gridOptions1"></div>
<!-- <div wm-data-grid grid-options="gridOptions2"></div> -->
</body>
</html>
/* Styles go here */
.grid {
display: table;
border-collapse: collapse;
}
.grid-head {
display: table-header-group;
}
.grid-header-row {
display: table-row;
background-color: #dedede;
}
.grid-body {
display: table-row-group;
}
.grid-row {
display: table-row;
}
.grid-cell {
display: table-cell;
border: 1px solid #ededed;
}
.content-container {
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.selected {
background-color: #efefef;
}
var gridTemplate = '' +
'<div ng-if="!gridOptions.data">Loading...</div>' +
'<div ng-if="gridManager.rows.length" wm-data-grid-search></div>' +
'<div class="grid" ng-if="gridManager.rows.length">' +
' <wm-data-grid-header></wm-data-grid-header>' +
' <div class="grid-body">' +
' <div class="grid-row" ng-class="{\'selected\': row.checked}" ng-repeat="row in gridManager.rows | filter: gridManager.searchText" wm-data-grid-row></div>' +
' </div>' +
'</div>' +
'<div ng-if="gridManager.allowAdd"><button ng-click="gridManager.addRow()">Add Row</button>' +
'<div ng-if="gridOptions.data && !gridOptions.data.length">No data found.</div>',
gridRowTemplate = '<div wm-data-grid-cell="checkboxCell"></div><div class="grid-cell" ng-repeat="col in gridManager.columnDefs"><wm-data-grid-cell></wm-data-grid-cell></div><div wm-data-grid-cell="deleteRowCell"></div>',
gridCellTemplate = '<div class="content-container" ng-style="{width:col.width}">{{row[col.field]}}</div>',
gridHeaderTemplate = '<div class="grid-head" ng-if="!gridManager.hideHeader"><div class="grid-header-row"><div wm-data-grid-cell="checkboxHeader"></div><div class="grid-cell" ng-repeat="col in gridManager.columnDefs"><div class="content-container" ng-style="{width: col.width}">{{col.displayName}}</div></div></div></div>',
gridSearchTemplate = '<div>Search: <input type="text" ng-if="gridManager.allowSearch" ng-model="gridManager.searchText"></div>',
gridFooterTemplate = '<div ng-show="gridOptions.showFooter">Grid Footer</div>',
gridCheckboxTemplate = '<div ng-show="gridOptions.showCheckboxes"><input type="checkbox" ng-checked="row.checked" ng-click="gridManager.toggleRowSelection($event, row)"></div>',
gridCheckboxHeaderTemplate = '<div ng-show="gridOptions.showCheckboxes"><input type="checkbox" ng-if="gridManager.multiselect" ng-checked="gridManager.allRowsChecked()" ng-click="gridManager.toggleSelectAll($event)"></div>',
editRowCellTemplate = '<div ng-if="gridManager.allowEdit" class="grid-cell" title="Edit Row">Edit</div>',
deleteRowCellTemplate = '<div ng-if="gridManager.allowDelete" class="grid-cell" title="Delete Row" ng-click="gridManager.deleteRow($index, row)">X</div>';
angular.module('GridApp').directive('wmDataGrid', function () {
return {
template: gridTemplate,
scope: {
gridOptions: '='
},
controller: function ($scope) {
this.scope_ = $scope;
this.scope_.gridManager = new GridManager();
this.scope_.rows = this.scope_.gridManager.rows;
this.scope_.columnDefs = this.scope_.gridManager.columnDefs;
this.templates = {};
this.scope_.$watch('gridOptions.data', angular.bind(this, function (newVal) {
if (angular.isDefined(newVal)) {
this.scope_.gridManager.init(this.scope_.gridOptions);
// Expose grid manager instance to controller.
this.scope_.gridOptions.gridManager = this.scope_.gridManager;
this.setTemplates();
}
}));
this.setTemplates = function () {
var options = this.scope_.gridOptions;
this.templates.header = options.headerTemplate || gridHeaderTemplate;
this.templates.row = options.rowTemplate || gridRowTemplate;
this.templates.cell = options.cellTemplate || gridCellTemplate;
this.templates.editRowCell = options.editRowCellTemplate || editRowCellTemplate;
this.templates.deleteRowCell = options.deleteRowCellTemplate || deleteRowCellTemplate;
this.templates.footer = options.footerTemplate || gridFooterTemplate;
this.templates.checkboxCell = options.checkboxTemplate || gridCheckboxTemplate;
this.templates.checkboxHeader = options.checkboxHeaderTemplate || gridCheckboxHeaderTemplate;
this.templates.search = options.searchTemplate || gridSearchTemplate;
};
this.scope_.getTemplate = function (type) {
return this.templates[type];
}.bind(this);
},
compile: function (element, attrs) {
return {
'pre': function (scope, element) {
console.log('grid pre compile...', element.scope());
},
'post': function (scope, element, attrs) {
window.console.log('grid linking function..');
}
};
}
};
});
angular.module('GridApp').directive('wmDataGridRow', function () {
return {
template: gridRowTemplate,
require: '^wmDataGrid',
scope: true,
link: function (scope, element, attrs, gridController) {
scope.xx = function() {
//Dummy function on grid row scope.
};
}
};
});
angular.module('GridApp').directive('wmDataGridHeader', ['$compile', function ($compile) {
return {
restrict: 'E',
require: '^wmDataGrid',
scope: true,
link: function (scope, element, attrs, gridController) {
var headerTemplate = gridController.scope_.getTemplate('header');
element.replaceWith($compile(headerTemplate)(scope));
}
};
}]);
angular.module('GridApp').directive('wmDataGridCell', ['$compile', function ($compile) {
var CELL_TYPES = [
'cell',
'editRowCell',
'deleteRowCell',
'checkboxCell',
'checkboxHeader'
];
return {
restrict: 'EA',
require: '^wmDataGrid',
scope: true,
link: function (scope, element, attrs, gridController) {
var cellType = attrs.wmDataGridCell && CELL_TYPES.indexOf(attrs.wmDataGridCell) != -1 ? attrs.wmDataGridCell : 'cell';
var cellTemplate = gridController.scope_.getTemplate(cellType);
element.replaceWith($compile(cellTemplate)(scope));
}
};
}]);
// angular.module('GridApp').directive('wmDataGridDeleteRow', ['$compile', function ($compile) {
// return {
// //restrict: 'E',
// require: '^wmDataGrid',
// scope: true,
// link: function (scope, element, attrs, gridController) {
// console.log('scope.. ', scope);
// if (gridController.scope_.gridManager.allowDelete) {
// var deleteRowCellTemplate = gridController.scope_.getTemplate('deleteRowCell');
// element.replaceWith($compile(deleteRowCellTemplate)(scope.$parent));
// }
// }
// };
// }]);
angular.module('GridApp').directive('wmDataGridSearch', function () {
return {
template: gridSearchTemplate,
require: '^wmDataGrid',
scope: true,
link: function (scope, element, attrs, gridController) {}
};
});
var GridManager = function () {
this.rows = [];
this.columnDefs = [];
this.templates = {};
};
GridManager.prototype.init = function (gridOptions) {
this.rows = gridOptions.data;
this.columnDefs = gridOptions.columnDefs;
if (gridOptions.showCheckboxes) {
// Maintain the selected row count in order to avoid iterating over
// all rows whenever we need to find checked rows.
this.selectedRowCount = this.getSelectedRowCount();
this.multiselect = gridOptions.multiselect || true;
// If multiselect is not allowed, maintain the current selected row.
if (!this.multiselect) {
// Will always hold one element - the currently selected row.
this.currentlySelectedRow = [];
}
}
this.allowEdit = gridOptions.allowEdit || false;
this.allowDelete = gridOptions.allowDelete || false;
// TODO(shubhangi): Make templates dynamic.
//if (this.allowDelete) {
//this.columnDefs.push({
//field: 'deleteRow',
//displayName: '',
//width: '10px',
//template: deleteRowCellTemplate
//});
//}
this.allowAdd = gridOptions.allowAdd || false;
this.allowSearch = gridOptions.allowSearch || false;
this.hideHeader = gridOptions.hideHeader || false;
};
GridManager.prototype.updateSelectedRowCount = function (selected) {
if (selected) {
this.selectedRowCount++;
} else {
this.selectedRowCount--;
}
};
GridManager.prototype.toggleRowSelection = function (e, row) {
row.checked = e.target.checked;
if (this.currentlySelectedRow && this.currentlySelectedRow.length) {
var previousSelection = this.currentlySelectedRow.pop();
previousSelection.checked = false;
this.currentlySelectedRow.push(row);
}
this.updateSelectedRowCount(row.checked);
};
GridManager.prototype.deleteRow = function (index, row) {
//TODO(shubhangi): Show a modal asking for confirmation before deleting.
this.rows.splice(index, 1);
// If the row was checked then decrement the rowSelectionCount when deleting it.
if (row.checked) {
this.updateSelectedRowCount(false);
console.log('row selection count.. ', this.selectedRowCount);
}
};
GridManager.prototype.addRow = function (index, row) {
this.rows.push({});
};
GridManager.prototype.toggleSelectAll = function (e) {
var selected = e.target.checked;
angular.forEach(this.rows, function (row) {
row.checked = selected;
}.bind(this));
// Update selected row count.
if (selected) {
this.selectedRowCount = this.getSelectedRowCount();
} else {
this.selectedRowCount = 0;
}
};
GridManager.prototype.getSelectedRows = function () {
var selectedRows = [];
angular.forEach(this.rows, function (row) {
if (row.checked) {
selectedRows.push(row);
}
});
return selectedRows;
};
GridManager.prototype.getSelectedRowCount = function () {
return this.getSelectedRows().length;
};
GridManager.prototype.allRowsChecked = function () {
return this.selectedRowCount == this.rows.length;
};
var app = angular.module('GridApp', []);
angular.module('GridApp').controller('AppController', ['$scope', '$timeout', function ($scope, $timeout) {
$scope.gridData1 = [{
name: "Moroni",
age: 50
}, {
name: "Tiancum",
age: 43
}, {
name: "Jacob",
age: 27
}, {
name: "Nephi",
age: 29
}, {
name: "Enos",
age: 34
}];
$scope.fieldDefs = [{
field: 'name',
displayName: 'Display Name',
width: '80px'
}, {
field: 'age',
displayName: 'Age',
width: '50px'
}];
$scope.gridData2 = [{
name: "Moroni",
age: 50
}, {
name: "Tiancum",
age: 43
}, {
name: "Jacob",
age: 27
}, {
name: "Nephi",
age: 29
}, {
name: "Enos",
age: 34
}];
$scope.fieldDefs = [{
field: 'name',
displayName: 'Display Name',
width: '80px'
}, {
field: 'age',
displayName: 'Age',
width: '50px'
}];
deleteRowCellTemp = '<div ng-if="gridManager.allowDelete" class="grid-cell" title="Delete Row" ng-click="removeRow($index, row)">xx</div>';
$scope.gridOptions1 = {};
$scope.gridOptions2 = {};
$scope.removeRow = function (index, row) {
console.log('Remove Row...');
};
$scope.removeRow = function (index, row) {
console.log('Remove Row...');
};
$timeout(function () {
$scope.removeRow = function (index, row) {
console.log('Remove Row...');
};
$scope.gridOptions1.data = $scope.gridData1;
$scope.gridOptions1.columnDefs = $scope.fieldDefs;
$scope.gridOptions1.showCheckboxes = true;
$scope.gridOptions1.allowEdit = true;
$scope.gridOptions1.allowDelete = true;
$scope.gridOptions1.allowAdd = true;
$scope.gridOptions1.allowSearch = true;
$scope.gridOptions1.searchableFields = [];
//$scope.gridOptions1.deleteRowCellTemplate = deleteRowCellTemp;
//$scope.gridOptions1.headerTemplate = '<div>Header...</div>';
$scope.gridOptions2.data = $scope.gridData2;
$scope.gridOptions2.columnDefs = $scope.fieldDefs;
$scope.gridOptions2.showCheckboxes = true;
$scope.gridOptions2.allowEdit = true;
$scope.gridOptions2.allowDelete = true;
$scope.gridOptions2.allowAdd = true;
$scope.gridOptions2.allowSearch = true;
$scope.gridOptions2.searchableFields = [];
//$scope.gridOptions2.deleteRowCellTemplate = deleteRowCellTemplate;
}, 2000, true);
}]);