Reputation: 2343
Here is my link function for my directive
function linkFunc(scope, element, attr){
// Detect Element Click Logic
scope.myCtrl.clickedElsewhere = {value: true};
$document.on('click', function(){
scope.myCtrl.clickedElsewhere.value = true;
scope.$apply();
});
element.on('click', function(){
event.stopPropagation();
scope.myCtrl.clickedElsewhere.value = false;
scope.$apply();
});
// End Detect Element Click Logic
}
As we can see, we used $document.on()
and scope.apply
, this means that for every click on anywhere in the document we will trigger a digest cycle. If we have alot of $watch
triggers this might cause the web page to slow down. Although this implementation is not every efficient, I can't think of other ways to detect on element click and off element click for expanding and contracting my element.
Can someone provide some insight?
Thanks
Upvotes: 3
Views: 2524
Reputation: 849
You could just create an click off directive
myApp.directive('clickOff', function($parse, $document) {
var dir = {
compile: function($element, attr) {
// Parse the expression to be executed
// whenever someone clicks _off_ this element.
var fn = $parse(attr["clickOff"]);
return function(scope, element, attr) {
// add a click handler to the element that
// stops the event propagation.
element.bind("click", function(event) {
event.stopPropagation();
});
angular.element($document[0].body).bind("click",function(event) {
scope.$apply(function() {
fn(scope, {$event:event});
});
});
};
}
};
return dir;
});
Usage:
<div ng-app="myApp">
<button ng-click="show=true;" click-off="show=false;">
Click Me
</button>
<div class="message" ng-show="show" ng-init="show = false">
You clicked on the button. Now click anywhere else...
</div>
</div>
Example: https://jsfiddle.net/oytdwyxj/
Upvotes: 2
Reputation: 171669
it would be a lot more efficient to check scope.myCtrl.clickedElsewhere.value
before calling $apply()
in the document click handler:
$document.on('click', function(){
if(!scope.myCtrl.clickedElsewhere.value){
scope.myCtrl.clickedElsewhere.value = true;
scope.$apply();
}
});
This will prevent needless digests when there is no change in it's status.
You could also remove this click listener and reapply it when you click on element:
function docHandler() {
if (!scope.myCtrl.clickedElsewhere.value) {
scope.myCtrl.clickedElsewhere.value = true;
scope.$apply();
$document.off('click');//remove event listener completely
}
}
element.on('click', function() {
event.stopPropagation();
scope.myCtrl.clickedElsewhere.value = false;
scope.$apply();
$document.on('click', docHandler);// add document listener
});
Upvotes: 3
Reputation: 538
You could create two directives, with a parent/child relationship between them.
Take a look at the accordion directive that I created, which I believe that is what you need to in order to figure out your situation.
Directives:
app.directive("accordion", function () {
return {
template: "<div ng-transclude></div>",
restrict: "E",
scope: {
closeall: "@"
},
transclude: true,
replace: true,
controller: function ($scope, $element, $attrs) {
var itensScope = [];
var addItemScope = function (scope) {
itensScope.push(scope);
};
var closeItens = function () {
if ($scope.closeall == "false") return;
angular.forEach(itensScope, function (scope) {
scope.showItem = false;
});
}
return {
addItemScope: addItemScope,
closeItens: closeItens
};
}
};
});
app.directive("accordionItem", function () {
return {
template: "<div><div class='item' ng-class='{itemClose: !showItem}'>{{title}}</div><div class='itemInformation' ng-show='showItem' ng-transclude></div></div>",
restrict: "E",
transclude: true,
replace: true,
scope: {
title: "@"
},
require: "^accordion",
link: function (scope, element, attrs, ctrl, transcludeFn) {
ctrl.addItemScope(scope);
element.bind("click", function () {
ctrl.closeItens();
scope.$apply(function () {
scope.showItem = !scope.showItem;
});
});
}
};
});
Usage:
<accordion closeall="true">
<accordion-item title="A">
<p>
A
</p>
</accordion-item>
<accordion-item title="B">
<p>
B
</p>
</accordion-item>
</accordion>
I created this example a while ago and it is available in my GitHub: https://github.com/rodrigobranas/branas-angular-ui
Upvotes: 0