Reputation: 12630
I am writing a directive which is really simple: it should wrap an input element in a div. I use transclusion for this. The problem is that it breaks ng-model for some reason. Please see this plunker for the full code: http://plnkr.co/edit/tYTsVUbleZV1iG6eMgjo
What can I do to make ng-model behave as expected?
Here's the code for the directive:
testapp.directive('wrapInput', [function () {
return {
replace: true,
transclude: 'element',
template: '<div class="input-wrapper" ng-transclude></div>'
};
}]);
Upvotes: 2
Views: 1253
Reputation: 327
I figured since ng-repeat
can do it, it must be possible:
http://plnkr.co/edit/stf02iPNvNnlsx1dsFZx?p=preview
app.directive( 'wrapThisThing', [ '$compile', function( $compile ) {
return {
restrict: 'A',
transclude: 'element',
link: function( scope, element, attrs, ctrl, transclude ) {
// Compile a new DOM context and bind it to our scope.
var template = angular.element( '<div class="wrap">' );
var context = $compile( template )( scope );
transclude( function( clone, theScope ) {
// To make this work, we need to remove our directive attribute from
// the clone before compiling it back to its original scope. If we
// don't do this, the compiler will re-evaluate our directive, creating
// an infinite loop.
// We can use the $attr property to figure out what attribute variation
// was used (ie. data-wrap-this-thing, x-wrap-this-thing, wrap:this:thing,
// etc.).
// See more about attribute normalization:
// http://www.bennadel.com/blog/2648-inspecting-attribute-normalization-within-directives-in-angularjs.htm
clone.removeAttr( attrs.$attr.wrapThisThing );
// Compile the transcluded element and bind it to its own scope. Then
// append it to our new context.
context.append( $compile( clone )( theScope ) );
});
element.replaceWith( context );
}
};
}]);
Upvotes: 3
Reputation: 7279
I believe you have to define an isolate scope and maybe a controller for your directive. Something like this:
return {
replace: true,
transclude: 'element',
scope: {},
controller: function(scope){},
template: '<div class="input-wrapper" ng-transclude></div>'
};
Then you just have to bind the scope of your directive to the parent scope. Unfortunately i can't get it to work in your plnkr just yet but I'll edit my answer when I do.
Upvotes: 0
Reputation: 1143
If you change your markup to look like this
<div wrap-input>
<input type="text" ng-model="anObject.anotherValue"/>
</div>
And then you can also use
transclude: true // instead of transclude: 'element'
It works just fine. I can't speak as to why, but I've seen this issue before when using both transclude: 'element' and replace: true. I'll have to dive into the source later to see what's going on. Reagrdless, the resulting markup with this workaround should be the same.
Upvotes: 3