ChristyPiffat
ChristyPiffat

Reputation: 379

AngularJS directive not calling function on parent scope

I have a directive that includes an image with a right mouse click event that shows a custom context menu. When the user clicks on an item in this context menu, I want to call a method on the parent controller. I have set up the context menu using Ian Walter's ng-context-menu. When I click on the context menu item, I do call the code in the directive. However, everything I have tried fails to call the controller's function. I am sure there is just something I forgot to assign as I am new to Angular.

The main view calls the directive as follows:

<div id="apptOverlay" ng-controller="apptCtrl as apptCtrl">
  <div class="apptList">
    <section data-ng-repeat="apptItem in apptCtrl.appointmentList" data-ng-model="apptCtrl.appointmentList" class="apptStaffList">
      <my-box appt="apptItem" status-list="apptCtrl.statusList" edit-appt="apptCtrl.editAppt(apptItem)" status-changed="apptCtrl.statusChanged(apptItem)"></my-box>
    </section>
  </div>
</div>

The directive's HTML is the following:

<section>
    <header>
        <img src="../../images/{{ appt.StatusIcon }}" data-context-menu context-menu-margin-bottom="10" data-target="menu-{{ appt.Id }}" />
        <div id="menu-{{ appt.Id }}" class="dropdown position-fixed">
            <ul class="dropdown-menu" role="menu">
                <li data-ng-repeat="status in statusList" >
                    <a id="status-{{ status.StatusId }}" data-ng-click="changeStatus(this)">
                        <img title="{{ status.StatusDescription }}" alt="{{ status.StatusDescription }}" src="../../images/{{ status.Icon }}" class="cellImage cellImageSmall" />
                        {{ status.StatusDescription }}
                    </a>
                </li>
            </ul>
        </div>
        {{ appt.LastName }}, {{ appt.FirstName }} 
    </header>
    <article>
        <ul>
            <li>Comments: {{ appt.Comment }}</li>
        </ul>
    </article>
</section>

The directive's code includes the following:

angular.module('app').directive('myBox', ['$parse', function ($parse) {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            appt: '=appt',
            editAppt: '&',
            statusList: '=',
            statusChanged: '&'
        },
        templateUrl: 'myBox.html',
        controller: function($scope) {
          $scope.changeStatus = function (item) {
              $scope.appt.CurrentStatusId = item.status.StatusId;
              $scope.statusChanged({ appointment: $scope.appt });
          };
        }
    };
}]);

The controller includes the following:

angular.module('app').controller('apptCtrl', ['$scope', function($scope) {
    var self = this;
    self.appointmentList = [];
    self.statusList = [];

    self.changeStatus = function (appointment) {
        var id = appointment.Id;
    };
}]);

As an aside, I have a double-click method in the directive that calls a function in the parent controller with no problem, but this uses $scope.$apply() inside an $elem.on('dblclick') method. When I try to call $scope.$apply() inside my directive's changeStatus function, it gives me an '$apply already in progress' error.

I have included my code in this Plunkr. Because it is part of a much larger project, I tried to extract only the important parts for this question. I have tried calling the directive function in both the controller and the link sections with the same results. I know that the context menu is not showing up quite correctly in the Plunk, but did not think the CSS was that important to this question.

Thanks in advance for any help!

Upvotes: 1

Views: 1587

Answers (1)

reddiky
reddiky

Reputation: 182

Use $emit / $on

http://plnkr.co/edit/WU54jP

   angular.module('app').directive('myBox', ['$parse', function ($parse) {
    return {
        restrict: 'E',
        replace: true,
        scope: {
            appt: '=appt',
            editAppt: '&',
            statusList: '=',
            statusChanged: '&'
        },
        templateUrl: 'myBox.html',
        controller: function($scope) {
          $scope.changeStatus = function (item) {
              $scope.appt.CurrentStatusId = item.status.StatusId;
              $scope.statusChanged({ appointment: $scope.appt });
              $scope.$emit('changeStatus', 'Look Ma, No Hands');
             // alert('Status changed in directive');
            };
        },
        link: function ($scope, $elem, $attr) {

            $elem.bind('dblclick', function () {
                $scope.editAppt($scope.appt);
            });

            $elem.on('dblclick', function () {
                // Need to do this to set the appointment in the edit box
                $scope.$apply();
            });

            ////var fnChangeStatus = $parse($attr.statusChanged);
            //$scope.changeStatus = function (item) {
                //$scope.appt.StatusId = item.status.StatusId;
                //$scope.statusChanged({ appointment: $scope.appt });
                //alert('Status changed in directive');
                ////fnChangeStatus({ appointment: $scope.appt });
            //};

            var drawBox = function () {
                $elem.css({
                    height: '150px',
                    backgroundColor: '#99ccff'
                });
            };

            drawBox();
        }
    };
}]);


angular.module('app').controller('mainCtrl', function($scope){
 // There is a main controller that has more general code for the entire application
});

angular.module('app').controller('apptCtrl', ['$scope', function($scope) {
    /****************************************************************************************************
        Properties
    *****************************************************************************************************/
    var self = this;
    self.appointmentList = [{Id: 1, Comment: 'Testing...', LastName: 'Test', FirstName: 'First', StatusId: 1, StatusIcon: 'Scheduled.png'}];
    self.statusList = [{StatusId: 1, Icon: 'scheduled.jpg', StatusDescription: 'Scheduled'}, {StatusId: 2, Icon: 'cancelled.jpg', StatusDescription: 'Cancelled'}];

    /****************************************************************************************************
        Methods
    *****************************************************************************************************/
    self.editAppt = function (appointment) {
        self.action = 'Edit';
        // editing appointment here...
        alert('Editing box in controller');
    };
    $scope.$on('changeStatus', function (event, data) {
      alert(data);
    });

    self.changeStatus = function (appointment) {
        var id = appointment.Id;
        // changing status here...
        alert('Changing status in controller');
    };
}]);

explanation: http://toddmotto.com/all-about-angulars-emit-broadcast-on-publish-subscribing/

Upvotes: 1

Related Questions