Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS : ng-controller on directive does not work on transcluded elements within directive

Here is my script:

angular.module('MyApp',[])
.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div ng-transclude></div></div>',
        link:function($scope,$element,$attrs){
        }
    };
})
.controller('SalutationController',['$scope',function($scope){
    $scope.target = "StackOverflow";
}])

and the html:

<body ng-app="MyApp">
    <my-salutation ng-controller="SalutationController">
        <strong>{{target}}</strong>        
    </my-salutation>
</body>

The problem is , when SalutationController is applied on my-salutation directive, $scope.target is not visible for transcluded element.But if I put ng-controller on <body> or on <strong> element, it works. As docs says, ng-controller creates new scope.

  • Who can explain, how that scope and the scope of the directive are interfering with each other in this case?

  • How can I put controller on directive? Any hints will be appreciated.

like image 686
Engineer Avatar asked Mar 22 '14 08:03

Engineer


People also ask

Can Angular applications ng app be nested within each other?

AngularJS applications cannot be nested within each other. Do not use a directive that uses transclusion on the same element as ngApp . This includes directives such as ngIf , ngInclude and ngView .

What is transclude in AngularJS directive?

The ng-transclude directive facilitates AngularJS to capture everything that is put inside the directive in the markup and use it somewhere in the directive's template.

Which directive do we use to inform AngularJS about the parts controlled by it?

The ngRef attribute tells AngularJS to assign the controller of a component (or a directive) to the given property in the current scope. It is also possible to add the jqlite-wrapped DOM element to the scope. The ngRepeat directive instantiates a template once per item from a collection.

What is restrict in AngularJS directive?

Note: When you create a directive, it is restricted to attribute and elements only by default. In order to create directives that are triggered by class name, you need to use the restrict option. The restrict option is typically set to: 'A' - only matches attribute name.


1 Answers

1) The problem is ng-transclude's scope is the sibling scope of your directive. When you put the ng-controller to a parent element, the scope created by ng-controller is parent scope of both your directive and ng-transclude. Due to scope inheritance, the transcluded element is able to bind the {{target}} correctly.

2) You could do that using custom transclusion to bind the scope yourself

.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div class="transclude"></div></div>',
        compile: function (element, attr, linker) {
            return function (scope, element, attr) {
                linker(scope, function(clone){
                       element.find(".transclude").append(clone); // add to DOM
                });

            };
        }
    };
})

DEMO

Or using the transclude function in the link function:

.directive('mySalutation',function(){
    return {
        restrict:'E',
        scope:true,
        replace:true,
        transclude:true,
        template:'<div>Hello<div class="transclude"></div></div>',
        link: function (scope, element, attr,controller, linker) {
           linker(scope, function(clone){
                  element.find(".transclude").append(clone); // add to DOM
           });
        }
    };
})

DEMO

like image 159
Khanh TO Avatar answered Nov 09 '22 22:11

Khanh TO