Reputation: 504
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/
Upvotes: 2
Views: 1066
Reputation: 9497
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.
Upvotes: 2
Reputation: 41678
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.
Upvotes: 2
Reputation: 3538
@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/
Upvotes: 1