Reputation: 2923
I am building an AngularJS directive. I want that directive to wrap other contents which have an ng-click inside of it. The resulting button does not do anything when clicked. Here is a simplified version of the code which I tried:
(HTML)
<div ng-app="someapp">
<div ng-controller="Ctrl1">
<h2>Example</h2>
<my-dir data-x="1">
<button ng-click="refresh()" id="refresh1">Refresh</button>
</my-dir>
<my-dir data-x="2">
<button ng-click="refresh()" id="refresh2">Refresh</button>
</my-dir>
</div>
</div>
(JavaScript)
var app = angular.module('someapp', []);
app.controller('Ctrl1', function($scope){ });
app.directive('myDir', function(){
return {
restrict: 'E',
scope: {},
template: '<div><p>Directive contents:</p><div ng-transclude></div></div>',
transclude: true,
link: function(scope, element, attrs){
$scope.y = attrs.x+1;
scope.refresh = function(){
console.log("Refresh Called, y = ", $scope.y);
}
}
};
});
How can I change it so that the button actually triggers the $scope.refresh() function?
Extra clarification:
I need local object information for the directive (there can be multiple of this directive in the single controller), so I create a new scope.
Upvotes: 1
Views: 5140
Reputation: 3437
As dcodesmith points out, the transcluded ng-click will be bound to the controller's scope rather than the directive's. Depending on what exactly you want to do, you may wish for this to be the behavior (since the transcluded content is not part of the directive, why should it call a method of the directive's scope?). Personally, I would declare the method on the controller's scope instead.
app.controller('Ctrl1', function($scope){
$scope.refresh = function() {
console.log("Refresh called.");
};
});
In this case, your directive should declare the isolate scope, even if there is nothing in it.
Update: Based on your comment, why not just include the button in the directive template? In this case, it will already be associated with the correct scope.
If there are some situations that you will not need the refresh button, then expose that as an option through the directive as an attribute:
<my-dir show-button='true'></my-dir>
// directive scope
scope: {
showButton: '='
}
The biggest problem that I have with this approach is using the "two-way-binding" operator (=) to cause 'true' and 'false' to be treated as expressions. I just don't like the feel of that very much.
Anyway, hopefully this solves your problem... One more comment, and I'm saying this not even knowing if what you are implementing is actually a refresh button, but if it is, I would take a minute and consider whether you really need a "refresh" button. Angular excels at eliminating refresh button!
Update 2:
I've created a plunkr that shows how I think I would handle your situation, especially if any of the miscellaneous controls are reused:
http://plnkr.co/edit/FhrSwcrSZScvCfhtCSjn
In this example, the two button directives are effectively children of the "videoPlayer" directive. Their logic is contained within that directive, but they are instantiated separately, and not required for the parent directive to operate. The "parent directive" (videplayer) simply exposes the API for the "children" to use. Also not that the methods of the parent are methods of the constructor, not the scope. I think this is very strange, but it is taken exactly from the angular documentation:
http://docs.angularjs.org/guide/directive (last example on the page)
Note that each videoPlayer directive will still have it's own isolate scope.
Upvotes: 3
Reputation: 9614
Remove the scope
object. There seems to be a conflict going on there.
Excerpt from Creating a Directive that Wraps Other Elements
The transclude option changes the way scopes are nested. It makes it so that the contents of a transcluded directive have whatever scope is outside the directive, rather than whatever scope is on the inside. In doing so, it gives the contents access to the outside scope.
app.directive('myDir', function(){
return {
restrict: 'E',
//scope: {}, remove this line.
template: '<div><p>Directive contents:</p><div ng-transclude></div></div>',
transclude: true,
link: function(scope, element, attrs){
console.log("X-attr", attrs.x);
scope.refresh = function(){
console.log("Refresh Called");
}
}
};
});
Upvotes: 0