AngularJS bootstrap popover custom directive

May 2024 ยท 4 minute read

I have created a directive using bootstrap custom popover. That works taking an input from a user for a group name and it has two buttons for apply that value to model and show that value on tooltip and a button to close the popover. I am using popover java script events , Problem is that single popover works perfectly but when I open another one this popover not closing itself. Need help in closing other popovers while one is open. Here is the plnk showing the directive.

Here is the code

var app = angular.module('myApp',[]); app.directive('customEditPopover', function () { return { restrict: 'A', template: '<span><i class="fa fa-tags" aria-hidden="true"></i></span>', scope: { 'myModel': '=', }, link: function (scope, el, attrs) { scope.label = attrs.popoverLabel; var btnsave = '#' + attrs.popoverSave; var btncancel = '#' + attrs.popoverCancel; var index = attrs.index; $(el).tooltip({ title: '' + (scope.myModel == undefined) ? "" : scope.myModel, container: 'body', placement: 'top' }); $(el).popover({ trigger: 'click', html: true, content: attrs.popoverHtml, placement: attrs.popoverPlacement, container: 'body' }) .on('inserted.bs.popover', function (e) { bindevents(); $('#popovertext' + index).val(scope.myModel); }).on('hidden.bs.popover', function () { Unbindevents(); }); function bindevents() { $(btnsave).bind('click', function () { var text = $('#popovertext' + index).val(); scope.myModel = text; $(el).tooltip('hide') .attr('data-original-title', text) .tooltip('fixTitle') toggle(); }); $(btncancel).bind('click', function () { toggle(); }); } function Unbindevents() { $(btnsave).unbind('click'); $(btncancel).unbind('click'); } function toggle() { $(el).popover('hide'); $(el).click(); } } }; }); app.controller('MyController',['$scope',function($scope){ $scope.list=[ { name:'ABC' }, { name:'DEF' }, { name:'GHI' }, { name:'KLM' } ]; }]); 

Need help closing other popover while opening next one.

3

2 Answers

You can close the other popovers when show.bs.popover is triggered as shown below : Updated Plunkr

$(el).popover({ trigger: 'click', html: true, content: attrs.popoverHtml, placement: attrs.popoverPlacement, container: 'body' }) .on('show.bs.popover', function () { var siblings = $(el).parent().parent().siblings(); siblings.each(function (each){ $(siblings[each]).find('span').popover('hide'); }); }); 
2

IMO, the most modular way would be to use a service to track all opened PopUps.

This gives multiple benefits.

  • You can extend the service to track different types of popups, allowing you to close all or open all or do whatever you need

  • It removes the logic from the controls and directives on keeping track of popups. Thus they can focus on 1 specific thing. You can also run multiple controllers and popups on a page allowing each controller/popup to be a discrete unit of work and not needing to worry about other controllers/popups stomping on their items.

  • The cons of doing this

  • As I commented earlier you can use the angular bootstrap directives for this. Making your code "cleaner", because all this is done for you. What is not done for you is closing all active popups. You can take the same service approach I have used and just plug it into the ng-bootstrap directives.
  • Binding/Unbinding the JQUERY events seems to become unresponsive when you click 8+ times. I do not have an answer for why that is, but the overall idea of using a service to keep track is still a valid one.
  • You shoudl be able to cut and paste this code into the plnkr or your dev box and it will work.

    // Code goes here var app = angular.module('myApp',[]); app.factory('popUpService', function() { var activePopups = []; // factory function body that constructs shinyNewServiceInstance return { AddPopup : function(el) { activePopups.push(el); }, CloseAllPopups : function(closeFunction){ for (var i = 0; i < activePopups.length; i++) { closeFunction(activePopups[i]) } } } }); app.directive('customEditPopover',['popUpService', function (popUpService) { return { restrict: 'A', template: '<span><i class="fa fa-tags" aria-hidden="true"></i></span>', scope: { 'myModel': '=', }, link: function (scope, el, attrs) { scope.label = attrs.popoverLabel; var btnsave = '#' + attrs.popoverSave; var btncancel = '#' + attrs.popoverCancel; var index = attrs.index; $(el).tooltip({ title: '' + (scope.myModel == undefined) ? "" : scope.myModel, container: 'body', placement: 'top' }); $(el).popover({ trigger: 'click', html: true, content: attrs.popoverHtml, placement: attrs.popoverPlacement, container: 'body' }) .on('inserted.bs.popover', function (e) { bindevents(); $('#popovertext' + index).val(scope.myModel); }).on('hidden.bs.popover', function () { Unbindevents(); }); function bindevents() { CloseAll(); popUpService.AddPopup($(el)); $(btnsave).bind('click', function () { var text = $('#popovertext' + index).val(); scope.myModel = text; $(el).tooltip('hide') .attr('data-original-title', text) .tooltip('fixTitle') closeCurrent(); }); $(btncancel).bind('click', function () { closeCurrent(); }); } function Unbindevents() { $(btnsave).unbind('click'); $(btncancel).unbind('click'); } function closeCurrent(){ $(el).popover('hide'); // $(el).click(); } function CloseAll() { popUpService.CloseAllPopups(function(popup){ popup.popover('hide'); // popup.click(); }); } } } }]); app.controller('MyController',['$scope',function($scope){ $scope.list=[ { name:'ABC' }, { name:'DEF' }, { name:'GHI' }, { name:'KLM' } ]; }]); 

    ncG1vNJzZmirpJawrLvVnqmfpJ%2Bse6S7zGiorp2jqbawutJoa2pqZmyEeoGOmqWgrZyWv6u%2FjJumqKyjqb%2BivIyppqmnppq%2Fbq%2FUrKuopV2ZtrOxwq2gr50%3D