Inline editing an expanding array with Angular
Mechanics behind a growing array of items that can be edited inline. This is the variant that expands the original collection inline. See also http://iweinfuld.posterous.com/expanding-collections-with-inline-edit-in-ang
by iweinfuld
HTML
<script src="http://code.angularjs.org/1.1.0/angular.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<link rel="stylesheet" href="http://twitter.github.com/bootstrap/assets/css/bootstrap.css">
<div ng-app="app" class="container" ng-controller="Ctrl">
<div class="row">
<div class="span5 well" >
<div editable-collection="item in items" expand-on="value">
<div class="row">
<input type="text" ng-model="item.value" class="span2"/>
<button class="btn span1 pull-right"
ng-click="remove($index)">Delete</button>
</div>
</div>
</div>
</div>
<div class="row"><div class="span5 well">items:{{items|json}}</div></div>
<div class="row"><div class="span5 well">overflow:{{overflow|json}}</div></div>
</div>
JavaScript
var $scope;
var app = angular.module('app', []);
/**
* Creates an ng-repeat with an overflow area to enable inline editing with overflow.
*
* Nesting is not supported.
*/
app.directive('editableCollection', function factory($compile) {
return {
link: function(scope, el, attrs, controller) {
//parse the element and attributes
if (!attrs.expandOn) {
throw 'Expected exand-on attribute on element: ' + el.html() + '\n but found: ' + JSON.stringify(attrs.$attr) + '\n';
}
var split = attrs.editableCollection.split(' ');
var elementName = split[0];
var collectionName = split[2];
if (split.length != 3 || split[1] != 'in') {
throw 'Expected editable collection defined as "{element} in {collection}" got: ' + attrs.editableCollection;
}
//Take the elements from this one to move to a child ng-repeat
var original = el.contents();
//Add the ng-repeat for the actual collection
var repeater = angular.element('<div/>');
repeater.attr('ng-repeat', attrs.editableCollection);
repeater.append(original);
//Add the ng-repeat for the overflow
scope[collectionName].push({});
el.append(repeater);
//Watch the first element and expand the overflow if needed
scope.lastOverflowExpander = function() {
return scope[collectionName].slice(-1)[0][attrs.expandOn];
};
scope.$watch('lastOverflowExpander()', function(value) {
console.log('expanding for ' + value);
if (value) {
scope[collectionName].push({});
}
});
//compile everything so that Angular can link the new stuff
$compile(el.contents())(scope);
}
};
});
app.controller('Ctrl', function($scope) {
...