There is a much simpler solution.
Parent Form Directive
First, establish an isolated scope for the parent form directive and import the my-model
attribute with 2-way binding. This can be done by specifying scope: { model:'=myModel'}
. There really is no need to specify prototypical scope inheritance because your directives make no use of it.
Your isolated scope now has the 'model' binding imported, and we can use this fact to compile and link child directives against the parent scope. For this to work, we are going to expose a compile
function from the parent directive, that the child directives can call.
.directive('myForm', function ($compile) {
return {
replace: true,
transclude: true,
scope: { model:'=myModel'},
template: '<div ng-form novalidate><div ng-transclude></div></div>',
controller: function ($scope, $element, $attrs) {
this.compile = function (element) {
$compile(element)($scope);
};
}
};
Child Field Directive
Now its time to setup your child directive. In the directive definition, use require:'^myForm'
to specify that it must always reside within the parent form directive. In your compile function, add the ng-model="model.{id attribute}"
. There is no need to figure out the name of the model, because we already know what 'model' will resolve to in the parent scope. Finally, in your link function, just call the parent controller's compile function that you setup earlier.
.directive('myFormName', function () {
return {
require: '^myForm',
scope: false,
compile: function (element, attrs) {
element.attr('ng-model', 'model.' + attrs.id);
return function(scope, element, attrs, parentCtrl) {
parentCtrl.compile(element);
};
}
};
});
This solution is minimal with very little DOM manipulation. Also it preserves the original intent of compiling and linking input form fields against the parent scope, with as little intrusion as possible.