Chocksmith Chocksmith - 6 months ago 54
jQuery Question

Event listener disappears inside ng-repeat

My HTML form has many questions I create using ng-repeat.
I want to create a pop-over for some questions.
When I put the button that triggers the pop-over outside the ng-repeat, it works. Inside the ng-repeat it does not.

This works (button before ng-repeat):

enter image description here

<div class="row">
<div class="col-md-12">
<button title="" data-toggle="popover" data-placement="top" data-trigger="hover" data-title="Popover on hover" data-content="And here's some amazing content. It's very engaging. Right?" class="btn btn-primary btn-wide">
hover
</button>
<div ng-repeat="q in config.questionnaire.questions">


Producing this:

enter image description here

This does not work (button after ng-repeat):

enter image description here

<div class="row">
<div class="col-md-12">
<div ng-repeat="q in config.questionnaire.questions">
<button title="" data-toggle="popover" data-placement="top" data-trigger="hover" data-title="Popover on hover" data-content="And here's some amazing content. It's very engaging. Right?" class="btn btn-primary btn-wide">
hover
</button>


Inspecting the button element on google chrome, I found that some event listeners were removed:

These are the event listeners for the button before the ng-repeat:

enter image description here

These are the event listeners for the button after the ng-repeat:

enter image description here

As you can see, mouseover and mouse out listeners disappeared.

Why?

What can I do?

Answer

If you are using Bootstrap with your Angular project, think of using Angular UI library.

https://angular-ui.github.io/bootstrap/#/popover

It provides many directives which make it easier to connect Bootstrap with Angular. I tested the uib-popover directive with ng-repeat and it works properly this way. The code is pretty simple:

//...

<div ng-repeat="t in main.testArray">
    <button uib-popover="My popover content" popover-title="Popover title" popover-trigger="mouseenter">{{t.text}}</button>
</div>

//...

I will try to investigate your problem further, but the library seems to be an easy workaround.


Edit

I think I also managed to figure out a solution without using external library. What I did was creating a directive which initializes Boostrap popover on specified element. This way you make sure that the popover function will be fired when elements are rendered. I guess this was the reason why it didn't work with your method. You probably invoked the popover function before elements were really rendered as ng-repeat does this with a delay.

And here is the code:

angular.module('moduleName')
    .directive('bootstrapPopover', function() {
        return {
             restrict: 'A',
             link: function (scope, element, attrs) { 
                  element.popover();
             }
        };
    });

And then use directive on element:

<div ng-repeat="t in main.test">
    <button data-toggle="popover" data-placement="top" data-trigger="hover" data-title="Test" data-content="Test" 
            class="btn btn-primary btn-wide" bootstrap-popover>hover {{t.text}</button>
</div>

However, I would still recommend trying the Angular UI out. I used it in the last Angular project and it was really helpful.

Comments