ulilicht
ulilicht

Reputation: 747

How to prevent an angular-bootstrap dropdown from closing (Unbind Event which was bound by a directive)

I am using the Angular-Bootstrap Dropdown. I want to prevent it from closing on click until the user closes it intentionally.

Default state is: The Dropdown closes when clicking somewhere in the Document.

I identified the relevant lines of code: (Line 12, dropdown.js)

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

You can find the full code here: Link to Github

I don't want to change the original sources of angular-bootstrap to keep my project open for updates.

My question:

How can i unbind an event bound by a Directive to the document in an Angular Controller?

Upvotes: 41

Views: 55288

Answers (9)

Ramki
Ramki

Reputation: 158

You can stop event from bubbling up in DOM Tree in angular 2 and above by adding event propagation. Ex: (click)="$event.stopPropagation()"

Upvotes: 4

Todd Palmer
Todd Palmer

Reputation: 1102

Here is what the code looks like using the approved angular-bootstrap auto-close method. Notice the auto-close attribute goes on the top <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>

Upvotes: 0

Anthony Huang
Anthony Huang

Reputation: 576

For those who are using Angular UI-Bootstrap 0.13.0 or later version, here is the cleaner way that states on the UI-Bootstrap documentation.

By default the dropdown will automatically close if any of its elements is clicked, you can change this behavior by setting the auto-close option as follows:

  • always - (Default) automatically closes the dropdown when any of its elements is clicked.

  • outsideClick - closes the dropdown automatically only when the user clicks any element outside the dropdown.

  • disabled - disables the auto close. You can then control the open/close status of the dropdown manually, by using is-open. Please notice that the dropdown will still close if the toggle is clicked, the esc key is pressed or another dropdown is open. The dropdown will no longer close on $locationChangeSuccess events.

Here is the link to the documentation: https://angular-ui.github.io/bootstrap/#/dropdown

Upvotes: 27

cstuncsik
cstuncsik

Reputation: 2786

You can decorate directives.

With this way you don't have to touch the original code and you can keep the original behaviour.

You can put a close button inside the dropdown

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

Upvotes: 0

Xspirits
Xspirits

Reputation: 217

You can also use this solution : https://gist.github.com/Xspirits/684beb66e2499c3ff0e5 Gives you a bit more of control over the dropdown, if that's ever needed.

Upvotes: 0

Dane
Dane

Reputation: 1511

I solved this by adding the following to my drop down-menu. This prevents the drop down from closing unless you click on the tag that opens it

<ul class="dropdown-menu" ng-click="$event.stopPropagation()">

Upvotes: 149

Erik Hunter
Erik Hunter

Reputation: 1157

This is another hack, but you could add a directive to stop the toggle event from propagating. For example something like this worked for my specific use case:

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

Adding this directive to an element should disable the auto close behavior:

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

Plunker Example Here

Upvotes: 3

Richard Zschech
Richard Zschech

Reputation: 656

This is an even more crude way of overriding it based on Rob Jacobs answer except that it prevents the ugly flickering ulilcht commented on:

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

Upvotes: 0

Rob J
Rob J

Reputation: 6629

This is a crude way of overriding it. You need to control the is-open attribute manually and hijack the on-toggle event, example:

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

Controller:

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

I would ask for a property on the dropdownConfig constant (something like autoClose) for a permanent solution.

Upvotes: 0

Related Questions