1

Directive

 myApp.directive('vlcControls', function ($compile, $rootScope, $timeout, $window, pictureboxConstants) {


    var linker = function (scope, element, attrs) {


    vlcPlayerCustomTemplate = 'some html goes here';


                scope.getVLC = function (name) {
                    if ($window.document[name]) {
                        return $window.document[name];
                    }
                    if ($window.navigator.appName.indexOf("Microsoft Internet") == -1) {
                        if ($window.document.embeds && $window.document.embeds[name])
                            return $window.document.embeds[name];
                    } else {
                        return $window.document.getElementById(name);
                    }
                }

                scope.doPlayPause = function (vlcPlayer, event) {
                    var vlc = scope.getVLC(vlcPlayer);
                    if (vlc) {

                        if (vlc.playlist.isPlaying) {
                            vlc.playlist.pause();
                            angular.element(event.target).children().removeClass('fa fa-pause font-12');
                            angular.element(event.target).children().addClass('fa fa-play font-12');

                        } else {
                            if (vlc.audio.mute == true) {
                                //do something
                            }
                            vlc.playlist.play();
                        }
                    }
                }

            angular.element(document.querySelector("#controls" + index)).append(element.html(vlcPlayerCustomTemplate));
            $compile(element.contents())(scope);
    };
    return {
        restrict: "E",
        link: linker
    };
});

Controller

myModule.controller('myModuleCtrl', function ($scope, $http, $controller, $compile) {

$compile("<vlc-controls></vlc-controls>")($rootScope);

});

What is the best way to only call specific directive function/method doPlayPause from controller without using vlcPlayerCustomTemplate html elements or any DOM manipulations ?

Aruna
  • 11,959
  • 3
  • 28
  • 42
Slimshadddyyy
  • 4,085
  • 5
  • 59
  • 121
  • Just I was wondering, do you have a chance to look at my answer below which works nicely with `$compile` as you expected without the `DOM` manipulations? – Aruna Nov 30 '16 at 03:57

4 Answers4

1

You should use $broadcast and $on to handle the communication from controller to directive.

To me, this is a code smell. If something is shared between parent and directive scope, it should be done through a binding. If you have to use the pub-sub pattern like this, you should rethink how you're using your directives, IMO.

Your directive should be able to do whatever it needs to without being called directly from the parent controller. If there's something it needs from the parent controller, it should be bound using attributes or a non-isolate scope.

Yatrix
  • 13,361
  • 16
  • 48
  • 78
0

One way is to $broadcast an event from the controller to directive and to listen in directive.

You can even send the parameters in the form of an object through it.

$rootScope.$broadcast('PlayPause',{}); will be in controller

in directive,

scope.$on('PlayPause',function(event, data){
      scope.doPlayPause(data.vlcPlayer, data.event)
});

Controller:

myApp.controller('MyCtrl', function($rootScope){
  var that = this;

  this.select = function(){
      $rootScope.$broadcast('PlayPause',{vlcPlayer: 'vlcPlayer',event: 'event'});
  }
})

Directive:

myApp.directive('vlcControls', function ($compile, $rootScope, $timeout, $window, pictureboxConstants) {


    var linker = function (scope, element, attrs) {


                scope.getVLC = function (name) {
                    if ($window.document[name]) {
                        return $window.document[name];
                    }
                    if ($window.navigator.appName.indexOf("Microsoft Internet") == -1) {
                        if ($window.document.embeds && $window.document.embeds[name])
                            return $window.document.embeds[name];
                    } else {
                        return $window.document.getElementById(name);
                    }
                }

                scope.doPlayPause = function (vlcPlayer, event) {
                    var vlc = scope.getVLC(vlcPlayer);
                    if (vlc) {

                        if (vlc.playlist.isPlaying) {
                            vlc.playlist.pause();
                            angular.element(event.target).children().removeClass('fa fa-pause font-12');
                            angular.element(event.target).children().addClass('fa fa-play font-12');

                        } else {
                            if (vlc.audio.mute == true) {
                                //do something
                            }
                            vlc.playlist.play();
                        }
                    }
                }

                 scope.$on('PlayPause',function(event, data){
                    scope.doPlayPause(data.vlcPlayer, data.event)
                });
    };
    return {
myApp.directive('vlcControls', function ($compile, $rootScope, $timeout, $window, pictureboxConstants) {


    var linker = function (scope, element, attrs) {


                scope.getVLC = function (name) {
                    if ($window.document[name]) {
                        return $window.document[name];
                    }
                    if ($window.navigator.appName.indexOf("Microsoft Internet") == -1) {
                        if ($window.document.embeds && $window.document.embeds[name])
                            return $window.document.embeds[name];
                    } else {
                        return $window.document.getElementById(name);
                    }
                }

                scope.doPlayPause = function (vlcPlayer, event) {
                    var vlc = scope.getVLC(vlcPlayer);
                    if (vlc) {

                        if (vlc.playlist.isPlaying) {
                            vlc.playlist.pause();
                            angular.element(event.target).children().removeClass('fa fa-pause font-12');
                            angular.element(event.target).children().addClass('fa fa-play font-12');

                        } else {
                            if (vlc.audio.mute == true) {
                                //do something
                            }
                            vlc.playlist.play();
                        }
                    }
                }

                 scope.$on('PlayPause',function(event, data){
                    scope.doPlayPause(data.vlcPlayer, data.event)
                });
    };
    return {
        restrict: "E",
        link: linker
    };
});      

The other way is to use a factory and share the method between the controller and directive,

Check this so post for more details

Community
  • 1
  • 1
Sravan
  • 18,467
  • 3
  • 30
  • 54
  • The directive when called from a controller, sets some DOM elements dynamically. The question here is how to call specific method from that directive assuming we do not want the DOM manipulation but only the functions. – Slimshadddyyy Nov 29 '16 at 06:32
  • since,`doPlayPause` is a function in the directive, `scope.doPlayPause(data.vlcPlayer, data.event)` just calls that function, `scope.doPlayPause()` without the parameters. – Sravan Nov 29 '16 at 06:37
  • Updated the directive where I added `vlcPlayerCustomTemplate`. I do not want that HTML but only want to call a function from that directive ? If I call the directive, it will also append HTML which I do not want but to call a function only. – Slimshadddyyy Nov 29 '16 at 06:44
  • If you just want to call a function, why cant you move it to a service or factory, directives are mainly intended to manipulate the dom, and if there is a directive in a HTML, it will definitely gets executed. The answer I have given can call a method in directive from a controller, have you checked that the DOM has changed? – Sravan Nov 29 '16 at 06:51
0

There are number of ways to do that, one of them is through broadcasting an event and listening to it in directive

However, I am not aware of the entire context and what exactly are you trying to achieve, but it looks like there is a small issue in a design. The question I would ask is "why would you want to call a method in a directive from parent controller?" Ideally, when you work with directive, it's functionality is self-contained and if you need to interact with outside world, then you either do it through variables binding or, as I already mentioned, events.

I am sure that there is a way to do exactly what you want, but given the information in the post, it is not necessary.

Shurik Agulyansky
  • 2,607
  • 2
  • 34
  • 76
  • Assume that the directive `` sets DOM elements alogn with some functions which can be reused. How can we call that functions defined in a directive from a controller ? – Slimshadddyyy Nov 29 '16 at 06:34
  • Again, similar to what @Yatrix mentioned, it looks like a code smell. You should look at this whole structure as separate modules, while each module has its own elements. Now, actions on these elements, do something within the module. And when that something happens, you can broadcast an event which will be listened by anyone who is interested in it. – Shurik Agulyansky Nov 29 '16 at 06:46
0

Your goal is to call the directive methods from the controller without any DOM manipulation and you can achieve the same with help of isolated scope as below.

You can have an isolated scope in your directive as below.

scope: {
           /* This is the reference object passed from the controller */
           controllerRef: '='
       }

Then you can pass the same from the controller,

$scope.vlcControls = {};

$compile('<vlc-controls controller-ref="vlcControls"></vlc-controls>')($scope);

Then you can assign the methods to this object in the directive,

      scope.controllerRef = scope.controllerRef || {};

      angular.extend(scope.controllerRef, {
         doPlayPause: scope.doPlayPause,
         // This is just for an example
         callDirectiveFromController: scope.callDirectiveFromController
      });

And you are now pretty much ready to call any of these methods from the controller as below,

$scope.vlcControls.callDirectiveFromController();

Check the code below and execute which is working nicely as you expected :-)

var myApp = angular.module('myApp', []);

myApp.controller('myModuleCtrl', function ($scope, $http, $controller, $compile) {
   $scope.vlcControls = {};
  
   $compile('<vlc-controls controller-ref="vlcControls"></vlc-controls>')($scope);
  
   $scope.vlcControls.callDirectiveFromController();

});

myApp.directive('vlcControls', function ($compile, $rootScope, $timeout, $window)//, pictureboxConstants) 
            {


    var linker = function (scope, element, attrs) {


                scope.getVLC = function (name) {
                    if ($window.document[name]) {
                        return $window.document[name];
                    }
                    if ($window.navigator.appName.indexOf("Microsoft Internet") == -1) {
                        if ($window.document.embeds && $window.document.embeds[name])
                            return $window.document.embeds[name];
                    } else {
                        return $window.document.getElementById(name);
                    }
                };

                scope.doPlayPause = function (vlcPlayer, event) {
                    var vlc = scope.getVLC(vlcPlayer);
                    if (vlc) {

                        if (vlc.playlist.isPlaying) {
                            vlc.playlist.pause();
                            angular.element(event.target).children().removeClass('fa fa-pause font-12');
                            angular.element(event.target).children().addClass('fa fa-play font-12');

                        } else {
                            if (vlc.audio.mute == true) {
                                //do something
                            }
                            vlc.playlist.play();
                        }
                    }
                };
      
           scope.callDirectiveFromController = function() {
              console.log('I called from the controller but executed inside the directive :-)');
           };
   
          scope.controllerRef = scope.controllerRef || {};

          angular.extend(scope.controllerRef, {
             doPlayPause: scope.doPlayPause,
             // This is just for an example
             callDirectiveFromController: scope.callDirectiveFromController
          });
    };
    return {
        restrict: "E",
        scope: {
          /* This is the reference object passed from the controller */
          controllerRef: '='
        },
        link: linker
    };
});

angular.bootstrap(document, ['myApp']);
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-controller="myModuleCtrl">
</div>
Aruna
  • 11,959
  • 3
  • 28
  • 42