Skip to content

Instantly share code, notes, and snippets.

@psyho
Created August 3, 2011 16:44
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save psyho/1123101 to your computer and use it in GitHub Desktop.
Save psyho/1123101 to your computer and use it in GitHub Desktop.
inline edit and dirty detection
<!DOCTYPE HTML>
<html xmlns:ng="http://angularjs.org">
<head>
<title>Inline Edit</title>
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.js" type="text/javascript"></script>
<script src="http://code.angularjs.org/0.9.18/angular-0.9.18.min.js" type="text/javascript" ng:autobind></script>
<style type="text/css">
.dirty {
border: 1px solid #00AA00;
}
</style>
<script type="text/javascript">
angular.service('data', function() {
var data = [];
for(var i = 0; i < 100; ++i) {
data.push({name: 'record '+i, id: i});
}
return data;
});
function FooController(data, $location) {
this.data = data;
this.$location = $location;
this.$watch('$location.hashSearch.page', this.resetFragments);
}
FooController.$inject = ['data', '$location'];
FooController.prototype = {
resetFragments: function() {
this.page = this.parsePage(this.$location.hashSearch.page);
this.updatePage(this.page);
this.masterFragment = this.data.slice(this.page * 10, (this.page+1)*10);
this.fragment = angular.copy(this.masterFragment);
},
parsePage: function(page) {
page = page || '0';
page = parseInt(page, 10);
if(page < 0) {
page = 0;
}
if(page * 10 >= this.data.length) {
page = Math.floor((this.data.length-1) / 10);
}
return page;
},
next: function() {
this.updatePage(this.page + 1);
},
prev: function() {
this.updatePage(this.page - 1);
},
updatePage: function(page) {
this.$location.update({hashSearch: {page: page}});
},
save: function() {
for(var i = 0; i < 10; ++i) {
this.data[this.page * 10 + i] = this.fragment[i];
}
this.resetFragments();
},
isDirty: function(rowIdx, property) {
return this.masterFragment[rowIdx][property] !== this.fragment[rowIdx][property];
}
};
angular.directive('ng:mark-dirty', function(expression, element) {
return function(element) {
var scope = this;
scope.$watch(expression, function() {
var isDirty = scope.$eval(expression);
element.toggleClass('dirty', isDirty);
});
}
});
function InlineEditController() {
this.$editing = false;
}
angular.widget('@ng:inline-edit', function(expression, element) {
var html = element.html();
var replacementHtml = '<div ng:show="$editing">'+html+'</div>'+
'<div ng:hide="$editing">{{'+expression+'}}</div>';
element.html(replacementHtml);
element.attr('ng:controller', 'InlineEditController');
this.descend(true);
this.directives(true);
return angular.extend(function($document, element) {
var scope = this;
function setEditing(val) {
return function() {
scope.$editing = val;
scope.$eval();
}
}
element.click(function(e) {
if (!scope.$editing) {
setEditing(true)();
}
e.stopPropagation();
});
element.keyup(function(e) {
if(e.keyCode === 13 || e.keyCode === 27) {
setEditing(false)();
}
});
element.find('input, textarea, select').blur(setEditing(false));
$document.click(setEditing(false));
}, {$inject: ['$document']});
});
</script>
</head>
<body ng:controller="FooController">
<table>
<thead>
<tr>
<th>ID</th>
<th>Name</th>
</tr>
</thead>
<tbody>
<tr ng:repeat="row in fragment">
<td ng:inline-edit="row.id" ng:mark-dirty="isDirty($index, 'id')">
<input type="text" ng:format="number" name="row.id" />
</td>
<td ng:inline-edit="row.name" ng:mark-dirty="isDirty($index, 'name')">
<input type="text" name="row.name" />
</td>
</tr>
</tbody>
</table>
<button ng:click="prev()">Prev</button>
<button ng:click="next()">Next</button>
Page: {{page}}
<br/>
<button ng:click="save()">Save</button>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment