Why won't $compile work with service using a directive?

151 views Asked by At

Please have a look at this example, since it is the best way to explain the problem.

In this example if you click the directive link, it does not compile the template, but instead displays it as "{{1+1}}".

On the other hand if you click the "Simple link" it compiles the template and displays "2" instead.

angular.module('myApp', [])
    .provider('$popup', function() {
        var service = {};

        this.$get = ['$compile', '$rootScope', function($compile, $rootScope) {
            var template = $('<div>{{1+1}}</div>');
            service.go = function() {
                $(document.body).append(template);
                $compile(template)($rootScope);
            }
            return service;
        }];
    })
    .directive('popupLink', ['$popup', function($popup) {
        return {
            restrict: 'A',
            scope: {},
            link: function link(scope, element, attrs) {
                element.click(function() {
                    $popup.go();
                    return false;
                });
            }
        };
    }])
    .controller('mainCtrl', ['$scope', '$popup', function($scope, $popup) {
        $scope.go = function() {
            $popup.go();
        };
    }])
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.1/angular.min.js"></script>

<div ng-app="myApp" ng-controller="mainCtrl">
    <a ng-href="/test" popup-link>Directive link</a>
    <a href="#" ng-click="go()">Simple link</a>
</div>

My question is why isn't the template compiling with the directive? (but it does in the controller)

And how do I fix it so that it compiles in the directive also?

P.S. Here is the jsbin link in case you want to play around with the code: http://jsbin.com/vuzutipedu/edit?html,js,output

2

There are 2 answers

1
georgeawg On BEST ANSWER

The directive needs to do scope.$apply():

link: function link(scope, element, attrs) {
    element.click(function() {
        $popup.go();
        //ADD apply
        scope.$apply();
        return false;
    });

The click event handler executes outside the AngularJS framework. A framework digest cycle needs to be performed to execute the watcher for the {{1+1}} interpolation.

It works with the ng-click directive because that directive includes scope.$apply.

For more information, see

DEMO

angular.module('myApp', [])
    .provider('$popup', function() {
        var service = {};

        this.$get = ['$compile', '$rootScope', function($compile, $rootScope) {
            var template = $('<div>{{1+1}}</div>');
            service.go = function() {
                $(document.body).append(template);
                $compile(template)($rootScope);
            }
            return service;
        }];
    })
    .directive('popupLink', ['$popup', function($popup) {
        return {
            restrict: 'A',
            scope: {},
            link: function link(scope, element, attrs) {
                element.click(function() {
                    $popup.go();
                  //ADD apply
                    scope.$apply();
                    return false;
                });
            }
        };
    }])
    .controller('mainCtrl', ['$scope', '$popup', function($scope, $popup) {
        $scope.go = function() {
            $popup.go();
        };
    }])
<script src="//unpkg.com/jquery"></script>
<script src="//unpkg.com/angular/angular.js"></script>

<div ng-app="myApp" ng-controller="mainCtrl">
    <a ng-href="/test" popup-link>Directive link</a>
    <a href="#" ng-click="go()">Simple link</a>
</div>

0
Ram_T On

Try this in $get, instead of $compile(template)($rootScope)

$compile(angular.element(template))(angular.element(template).scope());

Let me know if it works