Jer
Jer

Reputation: 5648

Why are mouse events from my directive not reflected in the view?

Here is an example fiddle. Please open the console first.

It's a little long, so to quickly explain: You'll see two divs. One is "regular", the other is created via a directive. They both have click event handlers (added via ng-click).

Clicking the regular (non-directive) div does what I expect - the appropriate variables in the scope are set and you see that in the view (top two lines in the output show the id and top of the div that was clicked).

The problem is when clicking the directive div. These variables are not updated. I know I'm misunderstanding something about the scope of the directive, but what is confusing me is the log messages that are printed (open the console when you do this). The clicks are registered, and the controller's scope does set the variable values, as you can see in the console.

Yet the html does not update - try clicking one div, then the other, and go back and forth, and you'll see.

What am I missing here?

Code (please don't be put off by its length!):

<div ng-app="MyApp">
    <div ng-controller="MyController">

        Top of the div you just clicked = {{top}}.<br/>
        Id of the div you just clicked = {{lastId}}.<br/>

        <div id="notADirective" class="myClass" ng-click="update($event)">
            Not a directive.  Click me.
        </div>

        <my-div element-id="isADirective" cls="myClass">
            I'm a directive.  Click me.
        </my-div>

</div>


angular.module('components', [])
    .controller('MyController', function ($scope) {

    $scope.lastId = '';
    $scope.top = '';

    $scope.update = function (event) {
        var myDiv = getFirstMyClassAncestor(event.target);
        var style = window.getComputedStyle(myDiv);
        $scope.top = style.top;
        $scope.lastId = myDiv.id;
        console.log("You just clicked " + $scope.lastId + " and its top is " + $scope.top);
    }

    function getFirstMyClassAncestor(element) {
        while (element.className.indexOf('myClass') < 0) {
            element = element.parentNode;
        }
        return element;
    }


}).directive('myDiv', function () {
    return {
        restrict: 'E',
        replace: true,
        transclude: true,
        controller: 'MyController',
        scope: {
            cls: '@',
            elementId: '@'
        },

        template: '<div id="{{elementId}}" class="{{cls}}" ng-click="update($event)"><div ng-transclude></div></div>'
    }
});

angular.module('MyApp', ['components'])

.myClass {
    background-color: #DA0000;
    position: absolute;
}
#isADirective {
    top: 300px;
}
#notADirective {
    top: 100px;
}

Upvotes: 1

Views: 1036

Answers (2)

Michael Kang
Michael Kang

Reputation: 52847

The angular expression bindings are outside of your directive's isolated scope. And you haven't imported lastId or top from your parent controller scope into your directives isolated scope, so there is no reason to expect that updating one variable in the inner directive scope will update the variable in the outer controller scope. There is no two way binding set up.

You can setup two way binding however using scope: {lastId: '=', top: '=' }.

Also you shouldn't be sharing controllers like you're doing. I suggest using anonymous controller functions.

Upvotes: 0

Reto Aebersold
Reto Aebersold

Reputation: 16624

In case of your directive the assigned controller MyController gets the scope of this directive and not the scope of div as you probably expect. I added a log statement for the different scope ids:

http://jsfiddle.net/6zbKP/4/

If you want to update the outer or parent scope you have to use a binding like this:

scope: {
        cls: '@',
        elementId: '@',
        callback: '='
},

Then bind your update function in the directive:

<my-div element-id="isADirective" cls="myClass" callback="update">
    I'm a directive.  Click me.
</my-div>

And call the callback in your directive:

 template: '<div id="{{elementId}}" class="{{cls}}" ng-click="callback($event)"><div ng-transclude></div></div>'

See http://jsfiddle.net/6zbKP/5/

Upvotes: 2

Related Questions