MW.
MW.

Reputation: 12630

ng-model breaks in transcluded input

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?

Directive code

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

Answers (3)

Stephen Bunch
Stephen Bunch

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

NicolasMoise
NicolasMoise

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

Adam
Adam

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

Related Questions