Angular call directive from controller

25,150

N0-$watch solutions:

1.
Provide your controller with a callback which receives an exposed interface from your directive. Your controller grabs the interface and uses it in script however it desires. Simple and can be implemented on any existing directive.

Plnkr for interface callback

  app.directive("simpleDialog",function(simpleDialog){
    return{
      template:"<button ng-click='open()'>open from directive</button>",
      scope:{
        onInit : "&onInit"
      },
      link: function(scope){
        scope.open = simpleDialog.open;
        scope.onInit({interface:{open:scope.open}});
      }
    }
  });

Much more complicated but an excellent pattern...

2.
If you wish to make a directive which also has a programmable interface then I suggest implementing the core of the directive as a provider. You could then implement your directive based on the provider and in cases where you wanted to access the same functionality completely through script, you could operate directly on the provider by injecting it into your controller.

This is the implementation strategy followed by ngDialog

Also, as you are are creating a confirmation dialog you would find this pattern useful as your open method could return a promise which can be resolved or rejected by your dialog, allowing your controller to respond based on the promise.

PLNKR DEMO

<!DOCTYPE html>
<html>

  <head>
    <script data-require="angular.js@*" data-semver="1.3.0" src="//code.angularjs.org/1.3.0/angular.js"></script>
    <link rel="stylesheet" href="style.css" />
    
  </head>

  <body ng-controller="myCtrl">
  
    <h1>Exposing a  Directive interface to a controller</h1>
    <div simple-dialog on-init="initCallback(interface)"></div>
    <p><button ng-click="open()">Open from controller</button></p>
    <p><button ng-click="open2()">Open from Exposed interface</button></p>
    
    <script>
      
      
      var app = angular.module("app",[]);
      
      app.controller("myCtrl",function(simpleDialog,$scope){
        $scope.open = simpleDialog.open;
        $scope.open2 = function(){
          this.interface.open();
        }
        $scope.initCallback = function(interface){
          this.interface = interface;
        }
      });
      
    
      app.provider("simpleDialog",function(){
        
        this.$get = function(){
          
          var publicMethods={
            open:function(){
              alert("Impelment Dialog Here");
            }
          }
          
          return publicMethods;
          
        }
      });
      
      app.directive("simpleDialog",function(simpleDialog){
        return{
          template:"<button ng-click='open()'>open from directive</button>",
          scope:{
            onInit : "&onInit"
          },
          link: function(scope){
            scope.open = simpleDialog.open;
            scope.onInit({interface:{open:scope.open}});
          }
        }
      });
      
      angular.bootstrap(document,["app"]);
      
    </script>
  </body>
</html>
Share:
25,150
Dominik Barann
Author by

Dominik Barann

Updated on July 09, 2022

Comments

  • Dominik Barann
    Dominik Barann almost 2 years

    I have the following directive for showing a popup to confirm an execution of a function on click.

    Now i would like to use it within my controller to show a popup if the properties of an object has been changed and the user wants to change the location without saving the object before. Is that possible?

    angular.module('app.confirm', [
        'ui.bootstrap',
        'template/modal/confirm.html',
    ])
    
    .controller('ConfirmModalController', ['$scope', '$modalInstance', function($scope, $modalInstance) {
        $scope.confirm = function() {
            $modalInstance.close();
        };
    
        $scope.cancel = function() {
            $modalInstance.dismiss('cancel');
        };
    }])
    
    .directive('confirm', ['$modal', function($modal) {
        return {
            restrict: 'A',
            scope: {
                confirm: '&',
                title: '@confirmTitle',
                message: '@confirmMessage',
                confirmButtonText: '@confirmButtonText',
                cancelButtonText: '@cancelButtonText'
            },
            link: function(scope, element, attributes) {
                element.bind('click', function() {
                    var modal= $modal.open({
                        controller: 'ConfirmModalController',
                        templateUrl: 'template/modal/confirm.html',
                        size: 'sm',
                        scope: scope
                    });
    
                    modal.result.then(function() {
                        scope.confirm();
                    }, function() {
                        // Modal dismissed
                    });
                });
            }
        };
    }]);
    
    angular.module('template/modal/confirm.html', []).run(['$templateCache', function($templateCache) {
        $templateCache.put(
            'template/modal/confirm.html',
            '<div class="modal-header" ng-show="title">' + 
                '<strong class="modal-title">{{title}}</strong>' + 
            '</div>' + 
            '<div class="modal-body">' +
                '{{message}}' + 
            '</div>' + 
            '<div class="modal-footer">' +
                '<a href="javascript:;" class="btn btn-link pull-left" ng-click="cancel()">{{cancelButtonText}}</a>' + 
                '<button class="btn btn-danger" ng-click="confirm()">{{confirmButtonText}}</button>' +
            '</div>'
        );
    }]);
    

    You can use it like that:

    <button 
        confirm="delete(id)" 
        confirm-title="Really?"
        confirm-message="Really delete?"
        confirm-button-text="Delete"
        cancel-button-text="Cancel"
        class="btn btn-danger"
    >
        Delete
    </button>