javascriptangularjsangularjs-directiveangularjs-serviceangularjs-compile

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


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


Solution

  • 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>