-1

I was trying to call a controller function from a directive in order to update a counter inside an hash-map.

After reading a few solutions, I ended up doing this:

'use strict';

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

dragDropApp.controller('DragDropCtrl', function($scope) {
    $scope.itemCount = {
        'item1' : {
            'count' : 0
        },
        'item2' : {
            'count' : 0
        },
        'item3' : {
            'count' : 0
        }
    };

    //this.updateItemCounter = function(itemId) {
    //    $scope.itemCount[itemId].count++;
    //}
    $scope.updateItemCounter = function(itemId) {
        $scope.itemCount[itemId].count++;
    }
}

dragDropApp.directive('droppable', function() {
    return {
        restrict : 'A',
        scope : {
            drop : '&', // parent
            bin : '=' // bi-directional scope
        },
        controller : 'DragDropCtrl',
        link : function(scope, element, attrs, DragDropCtrl) {
            var el = element[0];

            el.addEventListener('drop', function(e) {
                var item = document.getElementById(
                    e.dataTransfer.getData('Text')).cloneNode(true);

                //DragDropCtrl.updateItemCounter(item.id);
           >>>> scope.$parent.updateItemCounter(item.id); <<<<

                return false;
            }, false);
        }
    }
});

It works and does what I want, but I don't know if this approach is correct. Is it?

I've also tried to use the controller to access the function updateItemCounter, but the hash-map does not get updated, having the same values every time I call the function.

António Ribeiro
  • 4,129
  • 5
  • 32
  • 49
  • 1
    you may need to call `scope.$apply()` after `scope.$parent.updateItemCounter(item.id);` – akn Feb 02 '16 at 10:07
  • The `scope.$parent.updateItemCounter(item.id);` is already doing what I want. The thing is, am I following the right approach? Or should I follow the approach stated by the answer that you've posted? – António Ribeiro Feb 02 '16 at 10:41
  • 1
    It is not good to use `$parent`. You never know "who is the $parent". Eg. if you do `
    ` function is available on `scope.$parent.$parent.$parent.updateItemCounter` (because `ng-if` creates new child scope). It's better to do it like I described in my answer.
    – akn Feb 02 '16 at 11:12
  • Ok, then I'll change my code accordingly! Thank you :) – António Ribeiro Feb 02 '16 at 11:27
  • 1
    The best practice is to define callback functions as attributes of a directive as shown in the answer by @akn. Wiring your directive to a specific parent function with `scope.$parent` makes your directive less versatile. Follow the example of `ng-click`. If it called a specific function with `scope.$parent`, how useful would that be? – georgeawg Feb 02 '16 at 11:46
  • Thank you both guys! I ended up changing my code, taking into account the inputs given, and made use of the `$scope.apply()` instead. – António Ribeiro Feb 02 '16 at 13:31

1 Answers1

0

In html you should set attributes like this:

<div droppable bin="something" drop="updateItemCounter(itemId)"></div>

In directive if you want to call your controller's function you should call it like this. Note that property name of passed object must be the same as used in html

link : function(scope, element, attrs, DragDropCtrl) {
  ...
  scope.updateItemCounter({itemId: item.id}); 
  ...
}

if you wold like to pass more arguments you should use in html:

<div droppable drop="updateItemCounter(itemId, param2, param3)"></div>

and call:

scope.updateItemCounter({itemId: item.id, param2: 'something', param3: 'something'});

Read these answers:

Community
  • 1
  • 1
akn
  • 3,712
  • 26
  • 43
  • The thing is that I don't want to bind the **updateItemCounter** function to any attribute. I just want to call it from inside the directive's event listener function. – António Ribeiro Feb 02 '16 at 09:50