-1

I have a situation where I want to create custom component, which should be reusable and provide public API to change it's state. I am trying to achieve this by building component using directive and controller.

What I desire to do is simply:

customComponent.apiMethod1( Math.floor( Math.random() * 2 ) );

Here is JSFiddle which should explain my case: http://jsfiddle.net/7d7ad/4/

On line 9 ( when user clicks a button ), I want to call line 22 method ( custom component public API method ). Is there anyway to achieve this?

MyFantasy512
  • 690
  • 1
  • 9
  • 20
  • as you have isolated the scope, you cannot call the function in your directive from your scope. instead you can write your function with-in a service and reuse it in all the places – CodingNinja Jul 31 '14 at 10:50
  • You really need to revisit angular first. The way you are using JQuery selectors is really incorrect. – Malkus Aug 01 '14 at 01:57
  • What do you mean by that? The only thing I see wrong here is that DOM manipulation is handled by controller, not the directive. – MyFantasy512 Aug 01 '14 at 07:40

2 Answers2

2

You are looking for Providers. There are three different types: Factories, Services, and Providers. Each is a bit different you can take a look at this summary.

Providers can allow you to share common methods, functions and data between different areas of your application without duplicating code.

Short example - Fiddle

html

<div ng-app="myApp" ng-controller="testController">
    <button ng-click="ClickMe()">Random</button>
    {{display.value}}
</div>

javascript

angular.module('myApp', [])
.controller('testController', ['$scope','myService', function($scope, myService) {
    $scope.display =new myService();

    $scope.ClickMe = function() {
        $scope.display.apiMethod1();
    };
}])
.factory('myService', function() {
     function factory() {
          this.value = "Hello World";

          this.apiMethod1 = function() {
               this.value =  Math.floor( Math.random() * 2 );
          };
      }
      return factory;
});
Malkus
  • 3,686
  • 2
  • 24
  • 39
  • Can I then link this provider to directive, so each instance will have it's own 'data/api scope'? And how should one call provider method referencing directive instance? – MyFantasy512 Jul 31 '14 at 11:04
  • Depending on how you set the factory up you can have them not share scope, and yes you can extend the factory in you directive. That being said I think you are approaching the problem incorrectly. Why are you not using `ng-click` and other angular methods in your code. – Malkus Jul 31 '14 at 11:09
  • I updated my answer to better exemplify what I am suggested and included a working fiddle. – Malkus Jul 31 '14 at 11:34
1

You can, in addition to a service, use a parent directive with a controller.

Here is an example of how this might work (service example at the bottom):

app.directive('parentDir', function() {
    return {
        controller: function($scope, $element) {
            var childFuns = [];
            this.registerFun = function(func) {
                childFuns.push(func);
            }
            //we will call this using ng-click
            $scope.onClick = function(){
                childFuns.forEach(function(func){
                   func.call(null,5)
                });
            }
        }
    }
})

And in the child directive:

app.directive('customcomp', function() {
    return {
        restrict: 'E',
        scope: {},
        require: '^parentDir', //we "require" the parent directive's controller,
                               //which makes angular send it as the fourth
                               //argument to the linking function.
        template: '<h2>{{_currentNumber}}</h2>',
        link: function(scope, elm, attrs, ctrl) {
            scope._currentNumber = 0;
            scope.apiMethod1 = function(val) {
                scope._currentNumber = val;
            };
            //call the parent controller's registring function with the function
            ctrl.registerFun(scope.apiMethod1);
        }
    }
});

Each child directive would "register" a function, and those functions can be stored and called from the parent directive in any way you want.

Note that you should use ng-click for events with angular.

FIDDLE

And here is how it might look with a service:

app.service('funcs', function(){
    var funcs = [];
    this.register = function(func){ funcs.push(func)};   
    this.call = function(){
        funcs.forEach(function(func){
            func.call(null,5);
        })
    }
})

FIDDLE

Mosho
  • 7,099
  • 3
  • 34
  • 51
  • I tried to extend your second example, so that you can distinguish between instances of the same type ( here : http://jsfiddle.net/c3M2s/4/ ). I wonder is it really necessary to create service that holds reference to all this ( in my opinion this approch do not favor encapsulation or reusability ). – MyFantasy512 Jul 31 '14 at 11:45