The above answers unfortunately don't quite work. In particular, the compile stage does not have access to scope, so you can't customize the field based on dynamic attributes. Using the linking stage seems to offer the most flexibility (in terms of asynchronously creating dom, etc.) The below approach addresses that:
<!-- Usage: -->
<form>
<form-field ng-model="formModel[field.attr]" field="field" ng-repeat="field in fields">
</form>
// directive
angular.module('app')
.directive('formField', function($compile, $parse) {
return {
restrict: 'E',
compile: function(element, attrs) {
var fieldGetter = $parse(attrs.field);
return function (scope, element, attrs) {
var template, field, id;
field = fieldGetter(scope);
template = '..your dom structure here...'
element.replaceWith($compile(template)(scope));
}
}
}
})
I've created a gist with more complete code and a writeup of the approach.