Schovi
Schovi

Reputation: 1990

Angular directive transclude create new scope?

I am trying to create directive component for modal windows, which take care about modal behaviour such as opening, closing, taking care of zIndex etc.

Content of modal components is controlled by Controller. So far idea is good, but when i try to have

<modal ng-controller="MyController">
  Some content which will be transcluded with dynamic {{value}} from MyController
</modal>

It failed and does not render {{value}} I have to wrap modal into controller

<div ng-controller="MyController">
  <modal>
    Some content which will be transcluded with dynamic {{value}} from MyController
  </modal>
</div>

Is there any way, how to make first example works, or it is impossible and why angular do it that way?

There is full example with plunker at the end.

var app = angular.module('plunker', []);
app.directive("modal", function() {
  return {
    restrict:'E',
    replace:true,
    transclude:true,
    scope: false,
    template:'<div class="modal">Modal scope {{$id}}<div ng-transclude></div></div>',
    link: function($scope) {
      console.log("directive scope ", $scope.$id)
    }
  }  
})

app.controller('DetailControl', function($scope, $location) {
  console.log("controller scope ", $scope.$id)
  $scope.name = 'World';
});

and this HTML

<body>
main scope {{$id}}

Controller on same element as modal<br>

<modal ng-controller="DetailControl">
  <div>
    content scope (transclude) {{$id}}<br>
    Some content of modal window. The name is {{name || '-unknown-'}}
  </div>
</modal>

Controller outside modal
<div ng-controller="DetailControl">
  Controller scope {{$id}}
  <modal>
    <div>
      content scope (transclude) {{$id}}<br>
      Some content of modal window. The name is {{name || '-unknown-'}}
    </div>
  </modal>
</div>
<body>

here is plunker http://plnkr.co/edit/WOgZKB3e0bQUASMhFVOp?p=preview

Upvotes: 3

Views: 1900

Answers (2)

Daniel Dykszak
Daniel Dykszak

Reputation: 276

try use transclude with passing scope : http://docs.angularjs.org/api/ng.$compile

transcludeFn -> scope

"transcludeFn - A transclude linking function pre-bound to the correct transclusion scope. The scope can be overridden by an optional first argument. This is the same as the $transclude parameter of directive controllers. function([scope], cloneLinkingFn)."

Upvotes: 2

KayakDave
KayakDave

Reputation: 24676

The issue is the ngController directive creates it's own scope. When you do <modal ng-controller="MyController"> the ngController scope is a sibling to modal so modal can't see over (sideways in a sense) into that controller.

It works when ngController is a parent because you're using scope: false which causes your directive to inherit it's scope from its parent.

Rather than use a separate ngController directive you can attach a controller to your directive:

  app.directive("modal", function() {
    return {
      controller: function($scope, $location) {
        console.log("controller scope ", $scope.$id)
        $scope.name = 'World';
     }
  }  

This approach will give your directive good encapsulation as it no longer will depend on an external controller- which is good. One plus is you no longer need to coordinate multiple scopes.

If you need multiple directives to communicate you can use require to allow multiple directives to all share access to one parent directive's controller. This is the approach Angular internally takes (for instance in 'ng-switch`)

Upvotes: 2

Related Questions