web-dev-qa-db-fra.com

Comment empêcher la fermeture d'une liste déroulante de démarrage angulaire (Unbind Event lié par une directive)

J'utilise le menu déroulant Angular-Bootstrap. Je veux l'empêcher de se fermer en cliquant jusqu'à ce que l'utilisateur le ferme intentionnellement.

L'état par défaut est: La liste déroulante se ferme lorsque vous cliquez quelque part dans le document.

J'ai identifié les lignes de code pertinentes: (ligne 12, dropdown.js)

this.open = function( dropdownScope ) {
   if ( !openScope ) {
     $document.bind('click', closeDropdown); // line to unbind
     $document.bind('keydown', escapeKeyBind);
   }
}

Vous pouvez trouver le code complet ici: Lien vers Github

Je ne veux pas changer les sources originales de bootstrap angulaire pour garder mon projet ouvert aux mises à jour.

Ma question:

Comment puis-je dissocier un événement lié par une directive au document dans un contrôleur Angular?

38
ulilicht

J'ai résolu cela en ajoutant ce qui suit à mon menu déroulant. Cela empêche la liste déroulante de se fermer à moins que vous ne cliquiez sur la balise qui l'ouvre

<ul class="dropdown-menu" ng-click="$event.stopPropagation()">
137
Dane
25
Anthony Huang

Il s'agit d'un autre hack, mais vous pouvez ajouter une directive pour empêcher la propagation de l'événement bascule. Par exemple, quelque chose comme ça a fonctionné pour mon cas d'utilisation spécifique:

<div>
<div class="btn-group" dropdown is-open="status.isopen" ng-controller="DropDownCtrl">
  <button type="button" class="btn btn-primary dropdown-toggle" ng-disabled="disabled">
    Button dropdown <span class="caret"></span>
  </button>
  <ul class="dropdown-menu" role="menu">
    <li ng-click="goToPage('Action')">Action</li>
    <li disable-auto-close>Don't Dismiss</li>
    <li ng-click="goToPage('SomethingElse')">Something else here</li>
  </ul>
</div>

L'ajout de cette directive à un élément devrait désactiver le comportement de fermeture automatique:

angular.module('plunker', ['ui.bootstrap'])
.controller('DropDownCtrl', ['$scope', '$location',
function($scope, $location) {
  // Controller logic here
  $scope.goToPage = function(page) {
    console.log("Going to " + page + ". Dropdown should close");
    $location.path(page);
  };
}])
.directive('disableAutoClose', function() {
  // directive for disabling the default
  // close on 'click' behavior
  return {
        link: function($scope, $element) {
            $element.on('click', function($event) {
                console.log("Dropdown should not close");
                $event.stopPropagation();
            });
        }
    };
});

Exemple Plunker ici

3
Erik Hunter

Vous pouvez empêcher l'événement de se propager dans l'arborescence DOM dans angular 2 et au-dessus en ajoutant la propagation d'événement. Ex: (click) = "$ event.stopPropagation ()"

0
Ramki

Voici à quoi ressemble le code en utilisant la méthode de fermeture automatique angular-bootstrap approuvée. Notez que l'attribut de fermeture automatique va en haut <div>.

<div class="btn-group" uib-dropdown auto-close="disabled">
    <button id="single-button" type="button" class="btn btn-primary" uib-dropdown-toggle>
        Button dropdown <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" uib-dropdown-menu role="menu" aria-labelledby="single-button">
        <textarea class="form-control" ng-model="description" rows="4" placeholder="Description"></textarea>
    </ul>
</div>
0
Todd Palmer

C'est une façon encore plus grossière de la contourner basée sur la réponse de Rob Jacobs, sauf qu'elle empêche le vilain ulilcht vacillant commenté:

    $scope.toggled = function (open) {
        $scope.open = true;
        var child = $scope.$$childHead;
        while (child) {
            if (child.focusToggleElement) {
                child.isOpen = true;
                break;
            }
            child = child.$$nextSibling;
        }
    };
0
Richard Zschech

C'est une façon grossière de la contourner. Vous devez contrôler l'attribut is-open manuellement et détourner l'événement on-toggle, exemple:

<div class="btn-group" dropdown is-open="ctrl.isOpen" on-toggle="toggled(open)">
    <button type="button" class="btn btn-primary dropdown-toggle">
        Button dropdown <span class="caret"></span>
    </button>
    <ul class="dropdown-menu" role="menu">
        <li><a href="#">Action</a></li>
        <li><a href="#">Another action</a></li>
        <li><a href="#">Something else here</a></li>
        <li class="divider"></li>
        <li><a href="#">Separated link</a></li>
    </ul>
</div>

Manette:

    $scope.toggled = function (open) {
        $timeout(function () {
            $scope.ctrl.isOpen = true;
        });
    };

Je demanderais une propriété sur la constante dropdownConfig (quelque chose comme autoClose) pour une solution permanente.

0
Rob J

Vous pouvez également utiliser cette solution: https://Gist.github.com/Xspirits/684beb66e2499c3ff0e5 vous donne un peu plus de contrôle sur la liste déroulante, si cela est nécessaire.

0
Xspirits

Vous pouvez décorer des directives.

De cette façon, vous n'avez pas à toucher le code d'origine et vous pouvez conserver le comportement d'origine.

Vous pouvez mettre un bouton de fermeture dans la liste déroulante

HTML

<div class="dropdown-menu keep-dropdown-open-on-click" role="menu">
    <i class="icon-close close-dropdown-on-click"></i>
</div>

JS

angular.module('app').config(uiDropdownMenuDecorate);
uiDropdownMenuDecorate.$inject = ['$provide'];
function uiDropdownMenuDecorate($provide) {

    $provide.decorator('dropdownMenuDirective', uiDropdownMenuDecorator);

    uiDropdownMenuDecorator.$inject = ['$delegate'];

    function uiDropdownMenuDecorator($delegate) {

        var directive = $delegate[0];
        var link = directive.link;

        directive.compile = function () {
            return function (scope, elem, attrs, ctrl) {
                link.apply(this, [scope, elem, attrs, ctrl]);
                elem.click(function (e) {
                    if (elem.hasClass('keep-dropdown-open-on-click') && !angular.element(e.target).hasClass('close-dropdown-on-click')) {
                        e.stopPropagation();
                    }
                });
            };
        };

        return $delegate;
    }
}
0
cstuncsik