This is a follow-up question to this question: AngularJS input with focus kills ng-repeat filter of list
Basically my code is using AngularJS to pop-out a div (a dra
The drawer is not closing because the click event occurs outside the digest cycle and Angular doesn't know that $scope.open has changed. To fix it you can call $scope.$apply() after $scope.open is set to false, this will trigger the digest cycle.
$scope.toggleSearch = function () {
$scope.open = !$scope.open;
if ($scope.open) {
$scope.$window.onclick = function (event) {
closeSearchWhenClickingElsewhere(event, $scope.toggleSearch);
};
} else {
$scope.open = false;
$scope.$window.onclick = null;
$scope.$apply(); //--> trigger digest cycle and make angular aware.
}
};
Here is your Fiddle.
I was also trying to create a directive for the search drawer, if it helps (it needs some fixes :)). Here is a JSBin.
I couldn't find a solution i was 100% happy with, this is what i used:
<div class="options">
<span ng-click="toggleAccountMenu($event)">{{ email }}</span>
<div ng-show="accountMenu" class="accountMenu">
<a ng-click="go('account')">Account</a>
<a ng-click="go('logout')">Log Out</a>
</div>
</div>
the span with ng-click is used to open the menu, the div.accountMenu is toggled open or closed
$scope.accountMenu = false;
$scope.toggleAccountMenu = function(e){
if(e) e.stopPropagation();
$scope.accountMenu = !$scope.accountMenu;
if ($scope.accountMenu) {
$window.onclick = function(e) {
var target = $(e.target);
if(!target) return;
if(!target.hasClass('accountMenu') && !target.is($('.accountMenu').children())){
$scope.toggleAccountMenu();
}
};
} else if (!e) {
$window.onclick = null;
$scope.$apply();
}
}
This uses jQuery for child checking but you can probably do it without if needed.
I was getting some nasty errors with other peoples version, like trying to call $apply() when its already in a cycle, my version prevents propagation and safe-checks against $apply()
The accepted answer will throw an error if you click on the button to close the drawer/popup, and the button is located outside of it, because $apply()
will be executed twice.
This is a simplified version, that doesn't need call the whole toggleSearch()
function again and prevents the double $apply()
.
$scope.toggleSearch = function() {
$scope.open = !$scope.open;
if ($scope.open) {
$window.onclick = function(event) {
var clickedElement = event.target;
if (!clickedElement) return;
var clickedOnSearchDrawer = elementClasses.contains('handle-right') || elementClasses.contains('drawer-right') || (clickedElement.parentElement !== null && clickedElement.parentElement.classList.contains('drawer-right'));
if (!clickedOnSearchDrawer) {
$scope.open = !$scope.open;
$window.onclick = null;
$scope.$apply();
}
}
} else {
$window.onclick = null;
}
};
I suggest to add $event.stopPropagation(); on the view right after on the ng-click. You don't need to use jQuery.
<button ng-click="toggleSearch();$event.stopPropagation();">toggle</button>
Then, you can use this simplified js.
$scope.toggleSearch = function () {
$window.onclick = null;
$scope.menuOpen = !$scope.menuOpen;
if ($scope.model.menuOpen) {
$window.onclick = function (event) {
$scope.menuOpen = false;
$scope.$apply();
};
}
};