Slimshadddyyy
Slimshadddyyy

Reputation: 4073

AngularJS - Call specific Directive function/method from Controller

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 ?

Upvotes: 1

Views: 241

Answers (4)

Aruna
Aruna

Reputation: 12022

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>

Upvotes: 0

Sravan
Sravan

Reputation: 18647

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

Upvotes: 0

Alex A.
Alex A.

Reputation: 2603

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.

Upvotes: 0

Yatrix
Yatrix

Reputation: 13775

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.

Upvotes: 1

Related Questions