Question Vous ne savez pas comment masquer un div lorsque vous cliquez en dehors de la div


Ceci est une question de suivi à cette question: AngularJS entrée avec focus tue le filtre ng-repeat de la liste

Fondamentalement, mon code utilise AngularJS pour faire apparaître une div (un tiroir) à droite pour filtrer une liste de choses. La plupart du temps, l’UI est bloquée. En cliquant sur cette séquence, vous fermez le tiroir. Mais dans certains cas, nous ne bloquons pas l'interface utilisateur et nous devons autoriser l'utilisateur à cliquer en dehors du tiroir pour annuler la recherche ou sélectionner quelque chose d'autre sur la page.

Ma première idée était de fixer un gestionnaire window.onclick lorsque le tiroir s’ouvrait et si quelque chose était cliqué autre que le tiroir, il devait fermer le tiroir. C'est comme ça que je le ferais dans une application JavaScript pure. Mais dans Angular, c'est un peu plus difficile.

Voici un jsfiddle avec un exemple que j'ai basé sur l'exemple jsBin de @ Yoshi: http://jsfiddle.net/tpeiffer/kDmn8/

Le morceau de code correspondant à cet exemple est ci-dessous. Fondamentalement, si l'utilisateur clique en dehors du tiroir, j'appelle $ scope.toggleSearch pour que $ scope.open soit remis à false.

Le code fonctionne, et bien que $ scope.open passe de true à false, il ne modifie pas le DOM. Je suis sûr que cela a quelque chose à voir avec le cycle de vie des événements ou peut-être lorsque je modifie $ scope (car c'est à partir d'un événement séparé) qu'il s'agit d'une copie et non de l'original ...

Le but ultime à cet égard sera d’en faire une directive pour une réutilisation ultime. Si quelqu'un pouvait me diriger dans la bonne direction, je lui en serais reconnaissant.

$scope.toggleSearch = function () {

    $scope.open = !$scope.open;

    if ($scope.open) {
        $scope.$window.onclick = function (event) {
            closeSearchWhenClickingElsewhere(event, $scope.toggleSearch);
        };
    } else {
        $scope.$window.onclick = null;
    }
};

et

function closeSearchWhenClickingElsewhere(event, callbackOnClose) {

    var clickedElement = event.target;
    if (!clickedElement) return;

    var elementClasses = clickedElement.classList;
    var clickedOnSearchDrawer = elementClasses.contains('handle-right') 
        || elementClasses.contains('drawer-right') 
        || (clickedElement.parentElement !== null 
            && clickedElement.parentElement.classList.contains('drawer-right'));
    if (!clickedOnSearchDrawer) {
        callbackOnClose();
    }

}

26
2017-07-17 17:54


origine


Réponses:


Le tiroir ne se ferme pas car l'événement click se produit en dehors du cycle de lecture et Angular ne sait pas que $ scope.open a été modifié. Pour résoudre ce problème, vous pouvez appeler $ scope. $ Apply () après que $ scope.open soit défini sur false, cela déclenchera le cycle de digestion.

$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. 
    }
};

Voici votre Violon.

J'essayais aussi de créer une directive pour le tiroir de recherche, si cela aide (il faut des correctifs :)). Voici une JSBin.


21
2017-07-17 19:30



Je suggère d'ajouter $ event.stopPropagation (); sur la vue juste après sur le ng-clic. Vous n'avez pas besoin d'utiliser jQuery.

<button ng-click="toggleSearch();$event.stopPropagation();">toggle</button>

Ensuite, vous pouvez utiliser ce js simplifié.

$scope.toggleSearch = function () {
    $window.onclick = null;
    $scope.menuOpen = !$scope.menuOpen;

    if ($scope.model.menuOpen) {
        $window.onclick = function (event) {
            $scope.menuOpen = false;
            $scope.$apply();
        };
    }
};

12
2018-01-16 01:11



La réponse acceptée émettra une erreur si vous cliquez sur le bouton pour fermer le tiroir / menu contextuel, et que le bouton est situé à l’extérieur, car $apply() sera exécuté deux fois.

Ceci est une version simplifiée, qui n'a pas besoin d'appeler le tout toggleSearch() fonctionner à nouveau et empêche le 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;
  }
};

3
2017-09-08 15:23



Je n'ai pas trouvé de solution avec laquelle j'étais satisfait à 100%, c'est ce que j'ai utilisé:

<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>

le span avec ng-click est utilisé pour ouvrir le menu, le div.accountMenu est activé ou fermé

$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();
    }
}

Cela utilise jQuery pour la vérification des enfants, mais vous pouvez probablement le faire sans besoin.

J'obtenais des erreurs désagréables avec la version des autres utilisateurs, comme si j'essayais d'appeler $ apply () quand c'était déjà dans un cycle, ma version empêche la propagation et les contrôles sécurisés contre $ apply ()


2
2017-11-23 11:25