2

I am creating a drag directive to add an event listener to the dragstart event. I want to pass a function from my controller to the directive. When I add the event listener it will not invoke the passed in function.

Here is my controller:

angular.module('testApp').controller('testCtrl', [testCtrl]);

function testCtrl() {
    var vm = this;
    vm.dragStart = dragStart;

    function dragStart(e){
        alert('drag started!', e);
    }

}

Here is my directive:

angular.module('testApp').directive('testDraggable', function(){
    var directive = {
        scope: {
            dragStart: '&',
        },
        restrict: 'A',
        link: link
    };

    function link(scope, element, attrs){
        var dragStartCallback = function(event){
            alert('dragStartCallback!');
            scope.dragStart({e: event});
        }

        element[0].addEventListener('dragstart', dragStartCallback, false);
    }
    return directive;
});

The issue I am having is that the dragStartCallback function is called but the inner scope.dragStart function is never called. I've read about mapping the parameters which is what I'm doing and it is still failing. In the dragStartCallback I am getting the event passed in properly as well. If there is a better way to go about doing this any advice would be appreciated.

Thanks in advance for any of your input. There is a JS Fiddle here: http://jsfiddle.net/6sk4dbre/

3 Answers 3

2

As stated by @j.wittwer if you change the name of scope binding i.e.

scope: {
  dragStart:'&startDragging'
}

and then use it in directive as start-dragging="vm.startDrag(e)" it will work.

This is related to a feature introduced to ng-repeat (in version 1.2) where you can define header body and footer in repeated elements using ng-repeat-start and ng-repeat-end. You can find more about this syntax in documentation.

It seems rather unfortunate that this change that affects all directives (and attributes) in angular (at least in version 1.2.1) was only mentioned in migration guide and is really easy to miss especially if you don't know what to look for.

It's interesting however to know why this behaviour is present int angular.

The culprit code can be found in collectDirectives (line 5600 in version 1.2.1) where you can see:

var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
if (ngAttrName === directiveNName + 'Start') {
    attrStartName = name;
    attrEndName = name.substr(0, name.length - 5) + 'end';
    name = name.substr(0, name.length - 6);
}

In current source on github mentioned code is changed to:

var directiveNName = ngAttrName.replace(/(Start|End)$/, '');
if (directiveIsMultiElement(directiveNName)) {
    if (ngAttrName === directiveNName + 'Start') {

so the behaviour in question will only affect directives having multiElement: true which is a change introduced in this pull request.

Sign up to request clarification or add additional context in comments.

Comments

2

There is a problem with using the name dragStart in your directive scope binding. Change it to something else, and your code will work.

var directive = {
    scope: {
        ds: '&',
    },

http://jsfiddle.net/wittwerj/7vnxjxsq/

As pointed out by @miensol, this is due to a change documented in the Migrating from 1.0 to 1.2 guide:

Directives cannot end with -start or -end
This change was necessary to enable multi-element directives. The best fix is to rename existing directives so that they don't end with these suffixes.

Apparently this applies to attributes as well.

2 Comments

In my real code, not the example my scope function was named handleDragStart and it wouldn't execute even after renaming the directive scope to ds. So there is something with those two names that are the issue.
@j.wittwer just added a more details about this behaviour.
1

@j.wittwer is right that it had to do with the name of the element attribute dragStart but its not because of what the name is, its because for some reason Angular doesn't like hyphenated attributes while passing functions to directive attributes. This only happens for passing parent controller functions to a directive. If you make it dragstart instead of drag-start it would work fine, of course then your directive needs to have:

scope : {
    ds : '&dragstart'
}

http://jsfiddle.net/6sk4dbre/5/

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.