Angular+Handsontable
http://angularjs.org/
HTML
<div ng-controller="MyCtrl" id="x">
<div id="thetable"></div>
<pre>
{{pretty}}
</pre>
</div>
<div id="log"></div>
CSS
#x {
float: left;
width: 300px;
}
#log {
border: 1px solid black;
float: left;
margin-left: 20px;
width: 200px;
}
#log div:nth-child(even) {
background-color: #ddf;
}
/**
* It may be useful to copy the below styles and use on your page
*/
.handsontable {
position: relative;
font-family: Arial, Helvetica, sans-serif;
line-height: 1.3em;
font-size: 13px;
}
.handsontable * {
box-sizing: content-box;
-webkit-box-sizing: content-box;
-moz-box-sizing: content-box;
}
.handsontable table {
border-collapse: separate;
position: relative;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-o-user-select: none;
user-select: none;
border-spacing: 0;
}
.handsontable th,
.handsontable td {
border-right: 1px solid #CCC;
border-bottom: 1px solid #CCC;
min-width: 50px;
height: 22px;
line-height: 21px;
padding: 0 4px 0 4px; /* top, bottom padding different than 0 is handled poorly by FF with HTML5 doctype */
}
.handsontable div.minWidthFix {
width: 50px;
}
.handsontable tr:first-child th.htNoFrame,
.handsontable th:first-child.htNoFrame,
.handsontable th.htNoFrame {
border-left-width: 0;
background-color: white;
border-color: #FFF;
}
.handsontable th:first-child,
.handsontable td:first-child,
.handsontable .htNoFrame + th,
.handsontable .htNoFrame + td {
border-left: 1px solid #CCC;
}
.handsontable tr:first-child th,
.handsontable tr:first-child td {
border-top: 1px solid #CCC;
}
.handsontable thead tr:last-child th {
border-bottom-width: 0;
}
.handsontable thead tr.lastChild th {
border-bottom-width: 0;
}
.handsontable th {
background-color: #EEE;
color: #222;
text-align: center;
font-weight: normal;
white-space: nowrap;
}
.handsontable th .small {
font-size: 12px;
}
.handsontable thead th {
padding: 2px 4px;
}
.handsontable th.active {
background-color: #CCC;
}
/* border background */
.handsontable .htBorderBg {
position: absolute;
...
JavaScript
var myApp = angular.module('myApp',[]);
function log(msg) {
var e = document.getElementById("log");
e.innerHTML += "<div>" + msg + "</div>";
}
function MyObj(num, id, name, type, category) {
this.num = num;
this.id = id;
this.name = name;
this.type = type;
this.category = category;
if (category == null) {
this.category = "NEW";
}
}
MyObj.prototype.propUpdate = function(prop, value) {
var match = value == this[prop];
log("Updated property " + prop + " of " + this.num + ", values match: " + match);
};
function MyCtrl($scope) {
var elem = $("#thetable");
var newOne = { id: null, name: null, type: null, category: null };
$scope.obj = [
new MyObj(1, "a", "xyz", "type c", "P"),
new MyObj(2, "b", "xya", "type c", "P"),
];
elem.handsontable({
startRows: 0,
rowHeaders: true,
minSpareRows: 1,
colHeaders: ["ID", "Type", "Name", "Category"],
columns: [
{ data: "id" },
{ data: "type" },
{ data: "name" },
{ data: "category" },
],
data: $scope.obj,
dataSchema: newOne,
onChange: function(changes, source) {
if (source == "loadData") return;
var renderChanges = [];
log(JSON.stringify(changes));
for (var i = 0; i < changes.length; i++) {
var idx = changes[i][0];
var o = $scope.obj[idx];
if (!o.propUpdate) {
o = new MyObj($scope.obj.length - 1, o.id, o.type, o.name, o.category);
$scope.obj[idx] = o;
renderChanges.push([idx, "id", null, o.id]);
renderChanges.push([idx, "type", null, o.type]);
renderChanges.push([idx, "name", null, o.name]);
renderChanges.push([idx, "category", null, o.category]);
}
o.propUpdate(changes[i][1],...