Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

show or hide popover of a parent element from controller - angularjs

I have the following div and inside it is an input text. The div has a popover and I wish to show it whenever the input text is on focus. If the input text is out of focus, I wish to hide the popover. I am currently trying to do that with the following code:

HTML:

<div id="divParent" bs-popover
    data-trigger="focus click" 
    data-auto-close="1"
    data-placement="bottom"
    class="pswd-popover"
    data-template-url="people/user/user-profile/templates/password-requirements.html">
    <rp-form-input-text
        rp-model="page.userData.password"
        config="page.formConfig.password">
    </rp-form-input-text>
</div>  

MODEL:

model.password = inputTextConfig({
    id: "password",
    fieldName: "password",
    dataType: "password",
    required: false,
    maxLength: 24,
    modelOptions: {
        allowInvalid: true,
    },
    onFocus: model.getMethod("showPopover")
});

CONTROLLER:

vm.showPopover = function () {
    var focus = true;

    $(window).keyup(function (e) {
        var code = (e.keyCode ? e.keyCode : e.which);
        if (code == 9 && focus) {
            $timeout(function() {
                angular.element('#divParent').trigger('click');
            }, 100);
            focus = false;
        }
    });

};

The issue I'm having is I only want the onfocus function to trigger via tab. Because clicking the div automatically triggers the show of popover. That's why I have the keyup function in order to detect if the div was clicked or accessed via TAB. Another issue is I only show and hide the popover by triggering the onclick of the div. How can I show and hide the popover of the parent div from the controller?

like image 966
edsamiracle Avatar asked Jul 18 '17 06:07

edsamiracle


1 Answers

I've implemented this literally - trigger on tab only (and not when clicking the field), but I suspect you'll want to include both so you'll find the code for that scenario also below.

You can use the $popover service to have more precise control over triggering it.

var app = angular.module('app', ['ngAnimate', 'ngSanitize', 'mgcrea.ngStrap']);

app.controller('MainCtrl', function($scope, $popover) {

  // sometimes we don't want to trigger code to show the Popover
  $scope.suspendPopoverAction = false;

  $scope.popover = {
    title: 'HEY',
    content: 'This was triggered by tabbing.'
  };

  var asAServiceOptions = {
    title: $scope.popover.title,
    content: $scope.popover.content,
    trigger: 'manual',
    placement: 'bottom',
    autoClose: true,
    onBeforeShow: function() {
      $scope.activeElement = document.activeElement; // record element with focus
      $scope.suspendPopoverAction = true; // don't trigger blur code
    },
    onShow: function() {
      if ($scope.activeElement) $scope.activeElement.focus(); // restore focus
      $scope.suspendPopoverAction = false; // popup is showing, and focus is back to input, so now safe for blur code
    }
  };

  var myPopover = $popover(angular.element(document.querySelector('#divParent')), asAServiceOptions);

  $scope.inputHasFocus = function() {
    if (!$scope.suspendPopoverAction) {
      myPopover.$promise.then(myPopover.show);
    } else {
      $scope.suspendPopoverAction = false;
    }
  };

  $scope.inputLostFocus = function() {
    if (!$scope.suspendPopoverAction) {
      myPopover.$promise.then(myPopover.hide);
    }
  };

  $scope.inputClicked = function(event) {
    $scope.suspendPopoverAction = true; // prevent popover from showing on click

    // NB: If you want to allow mouse clicks also:
    // 1) use ng-click instead of ng-mousedown in the <input>
    // 2) remove "$scope.suspendPopoverAction = true;" line and replace with:
    //      event.stopPropagation();
    // Doing the above prevents the click triggering the "autoClose: true" option, resulting in flickering of the Popover
  };
});
body {
  padding: 5px !important;
}

.pswd-popover {
  background-color: orange;
  padding: 10px;
  margin: 10px;
}

.myheading {
  margin-bottom: 15px;
}
<!DOCTYPE html>
<html ng-app="app">

<head>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="//cdn.jsdelivr.net/fontawesome/4.5.0/css/font-awesome.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/bootstrap/3.3.6/css/bootstrap.min.css">
  <link rel="stylesheet" href="//mgcrea.github.io/angular-strap/styles/libs.min.css">
  <link rel="stylesheet" href="//mgcrea.github.io/angular-strap/styles/docs.min.css">
  <script src="//cdn.jsdelivr.net/angularjs/1.5.5/angular.min.js" data-semver="1.5.5"></script>
  <script src="//cdn.jsdelivr.net/angularjs/1.5.5/angular-animate.min.js" data-semver="1.5.5"></script>
  <script src="//cdn.jsdelivr.net/angularjs/1.5.5/angular-sanitize.min.js" data-semver="1.5.5"></script>
  <script src="//mgcrea.github.io/angular-strap/dist/angular-strap.js" data-semver="v2.3.8"></script>
  <script src="//mgcrea.github.io/angular-strap/dist/angular-strap.tpl.js" data-semver="v2.3.8"></script>
  <script src="//mgcrea.github.io/angular-strap/docs/angular-strap.docs.tpl.js" data-semver="v2.3.8"></script>
</head>

<body ng-controller="MainCtrl">

  <h4 class = "myheading">Trigger Popover on Tabbing in Password field only</h4>
  An input for testing Tab:
  <input type="text">

  <div id="divParent" class="pswd-popover">
    Password:
    <input type="text" ng-focus="inputHasFocus()" ng-blur="inputLostFocus()" ng-mousedown="inputClicked($event)">
    <button>Some Button</button>
  </div>

  Another input for testing Tab:
  <input type="text">

</body>

</html>

To show the Popover when either tabbing or clicking the password field:

var app = angular.module('app', ['ngAnimate', 'ngSanitize', 'mgcrea.ngStrap']);

app.controller('MainCtrl', function($scope, $popover) {

  // sometimes we don't want to trigger code to show the Popover
  $scope.suspendPopoverAction = false;

  $scope.popover = {
    title: 'HEY',
    content: 'Triggered by tabbing OR clicking.'
  };

  var asAServiceOptions = {
    title: $scope.popover.title,
    content: $scope.popover.content,
    trigger: 'manual',
    placement: 'bottom',
    autoClose: true,
    onBeforeShow: function() {
      $scope.activeElement = document.activeElement; // record element with focus
      $scope.suspendPopoverAction = true; // don't trigger blur code
    },
    onShow: function() {
      if ($scope.activeElement) $scope.activeElement.focus(); // restore focus
      $scope.suspendPopoverAction = false; // popup is showing, and focus is back to input, so now safe for blur code
    }
  };

  var myPopover = $popover(angular.element(document.querySelector('#divParent')), asAServiceOptions);

  $scope.inputHasFocus = function() {
    if (!$scope.suspendPopoverAction) {
      myPopover.$promise.then(myPopover.show);
    } else {
      $scope.suspendPopoverAction = false;
    }
  };

  $scope.inputLostFocus = function() {
    if (!$scope.suspendPopoverAction) {
      myPopover.$promise.then(myPopover.hide);
    }
  };

  $scope.inputClicked = function(event) {
    // using the below code prevents the click triggering the "autoClose: true" option resulting in flickering
    event.stopPropagation();
  };
});
body {
  padding: 5px !important;
}

.pswd-popover {
  background-color: orange;
  padding: 10px;
  margin: 10px;
}

.myheading {
  margin-bottom: 15px;
}
<!DOCTYPE html>
<html ng-app="app">

<head>
  <meta charset="utf-8" />
  <link rel="stylesheet" href="//cdn.jsdelivr.net/fontawesome/4.5.0/css/font-awesome.css">
  <link rel="stylesheet" href="//cdn.jsdelivr.net/bootstrap/3.3.6/css/bootstrap.min.css">
  <link rel="stylesheet" href="//mgcrea.github.io/angular-strap/styles/libs.min.css">
  <link rel="stylesheet" href="//mgcrea.github.io/angular-strap/styles/docs.min.css">
  <script src="//cdn.jsdelivr.net/angularjs/1.5.5/angular.min.js" data-semver="1.5.5"></script>
  <script src="//cdn.jsdelivr.net/angularjs/1.5.5/angular-animate.min.js" data-semver="1.5.5"></script>
  <script src="//cdn.jsdelivr.net/angularjs/1.5.5/angular-sanitize.min.js" data-semver="1.5.5"></script>
  <script src="//mgcrea.github.io/angular-strap/dist/angular-strap.js" data-semver="v2.3.8"></script>
  <script src="//mgcrea.github.io/angular-strap/dist/angular-strap.tpl.js" data-semver="v2.3.8"></script>
  <script src="//mgcrea.github.io/angular-strap/docs/angular-strap.docs.tpl.js" data-semver="v2.3.8"></script>
</head>

<body ng-controller="MainCtrl">

  <h4 class = "myheading">Trigger Popover on Tabbing or Clicking in Password field</h4>
  An input for testing Tab:
  <input type="text">

  <div id="divParent" class="pswd-popover">
    Password:
    <input type="text" ng-focus="inputHasFocus()" ng-blur="inputLostFocus()" ng-click="inputClicked($event)">
    <button>Some Button</button>
  </div>

  Another input for testing Tab:
  <input type="text">

</body>

</html>

Also note the subtle change in the HTML. This version uses <input ng-click="", whereas the tab-only code used <input ng-mousedown="". This change prevents the flickering that is associated with auto-close: true.

like image 76
K Scandrett Avatar answered Sep 28 '22 02:09

K Scandrett