Search code examples
javascriptangularjsangularjs-directivesoundmanager2

Why is `replace: true` Deprecated in AngularJS Directive


I am working on a sample player using angularjs, utilizing an element directive. I would like the events within the directive's template to remain contained within the directive. In other words, how can I use the controller in the directive to create events scoped only to elements within the directive's template.

The directive:

logsApp.directive('logPlayer', function() {
    return {
        restrict: 'E',
        replace: true,
        scope: {audio: '='},
        template: '<div ng-repeat="sample in audio">' +
            '{{sample.title}}' +
            '<button type="button" class="btn btn-default" ng-click="play()">Play</button>' +
            '<button type="button" class="btn btn-danger" ng-click="stop()">Stop</button>' +
            '</div>'
    };
});

The only way I can get the ng-click events to work is place the event methods in the parent controller's $scope:

logsApp.controller('LogListController', ['$scope', '$rootScope', 'LogsService', 'SoundManager', function($scope, $rootScope, LogsService, SoundManager) {
    ...
    $scope.play = function() {
    SoundManager.sound.createSound({
        id: SoundManager.idPrefix + this.sample.id,
        url: this.sample.filename,
        autoLoad: true,
        autoPlay: false,
        onload: function() {
            this.setPosition(0);
            this.play({
                whileplaying: function() {
                    ...
                }
            });
        }
    });
};

$scope.stop = function() {
    SoundManager.sound.stopAll();
};

How would I accomplish getting the play() and stop() events to be contained within the directive controller?


Solution

  • When I create a controller to the directive, and apply $scope.play = function() {}; nothing happens.

    The problem that you are having is that you are using replace: true which is deprecated. Remove that and your directive's controller will see the click events.

    angular.module("myApp").directive('logPlayer', function() {
        return {
            restrict: 'E',
            //replace: true,
            scope: {audio: '='},
            controller: function($scope) {
                $scope.play = function(index) {
                    console.log("play clicked "+index);
                };
            },
            template: '<div ng-repeat="sample in audio">' +
            '{{sample.title}}' +
            '<button type="button" ng-click="play($index)">Play</button>' +
            '<button type="button" ng-click="stop($index)">Stop</button>' +
            '</div>'
        };
    });
    

    The DEMO on JSFiddle.

    From the Docs:

    replace ([DEPRECATED!], will be removed in next major release - i.e. v2.0)

    specify what the template should replace. Defaults to false.

    • true - the template will replace the directive's element.
    • false - the template will replace the contents of the directive's element.

    -- AngularJS Comprehensive Directive API

    From GitHub:

    Caitp-- It's deprecated because there are known, very silly problems with replace: true, a number of which can't really be fixed in a reasonable fashion. If you're careful and avoid these problems, then more power to you, but for the benefit of new users, it's easier to just tell them "this will give you a headache, don't do it".

    -- AngularJS Issue #7636

    In this case the isolate scope of the directive is fighting the inherited scope of the ng-repeat directive. So either remove replace: true or remove the ng-repeat from the top element of the template.


    Update

    Note: replace: true is deprecated and not recommended to use, mainly due to the issues listed here. It has been completely removed in the new Angular.

    Issues with replace: true

    For more information, see AngularJS $compile Service API Reference - Issues with replace:true.