Skip to content
This repository has been archived by the owner on May 29, 2019. It is now read-only.

bug(tooltip): $apply already in progress #516

Closed
blabno opened this issue Jun 12, 2013 · 12 comments
Closed

bug(tooltip): $apply already in progress #516

blabno opened this issue Jun 12, 2013 · 12 comments

Comments

@blabno
Copy link

blabno commented Jun 12, 2013

Tooltip throws exceptions if bound to button and ngClick takes long.
HTML:

<button ng-click="exitEdit()" tooltip="Exit edit mode">exit</button>

JavaScript:

    $scope.exitEdit = function ()
    {
        if (this.isModified()) {
            if (!window.confirm("There are changes. Do you want to quit without saving?")) {
                return false;
            }
            ...
    }

The window.confirm pauses execution, which happens in $apply phase so the tooltip hits the same phase as it's triggered by jQuery (outside of Angular):

Error: $apply already in progress
beginPhase@http://localhost:8000/libs/angular.js:8288
Scope.$apply@http://localhost:8000/libs/angular.js:8090
hideTooltipBind@http://localhost:8000/libs/angular-ui-bootstrap.js:3451
jQuery.event.special[orig].handle@http://localhost:8000/libs/jquery.js:3431
jQuery.event.dispatch@http://localhost:8000/libs/jquery.js:3074
jQuery.event.add/elemData.handle@http://localhost:8000/libs/jquery.js:2750
(?)() angular.js (line 5704)
anonymous() angular.js (line 4800)
$apply(expr=undefined) angular.js (line 8093)
hideTooltipBind () angula...trap.js (line 3451)
handle(event=Object { originalEvent=Event mouseout, type="mouseout", timeStamp=0, more...}) jquery.js (line 3431)
dispatch(event=Object { originalEvent=Event mouseout, type="mouseout", timeStamp=0, more...}) jquery.js (line 3074)
handle(e=mouseout clientX=1132, clientY=104) jquery.js (line 2750)

@pkozlowski-opensource
Copy link
Member

@blabno Could you please provide a plunker with a minimal reproduce scenario? I'm not sure how you are getting into this error scenario.

Anyway, we need a reproduce scenario to progress.

@blabno
Copy link
Author

blabno commented Jun 12, 2013

http://plnkr.co/edit/H1LgxDHSFeHsPiW0rgvz?p=preview

btw. this error happens on firefox. On chrome you will not see it.

@pkozlowski-opensource
Copy link
Member

Indeed, can see this in FFox only. But this one is really strange... No idea what is going on here, looks like a hide trigger is firing when a confirmation dialog gets opened...

@sudhakar
Copy link

sudhakar commented Jul 7, 2013

I stumbled across the same issue today when I tried to use the custom triggers of tooltip for a tourIt directive. Using custom triggers appears to be only way in which tooltips can be controlled via code. But I couldnt use custom event because of this issue.

When tooltip is triggered with a custom event, angular throws $apply already in progress. You can see this issue in this plunker http://plnkr.co/edit/ptCe3hJLtYTaYrSgrTAu

This is reproducible both in chrome and firefox. Let me know if you need a PR.

@pkozlowski-opensource
Copy link
Member

@sudhakar What you are doing in your plunker doesn't make much sense as you are using a AngularJS-powered click-event handler to trigger another event. In this case the ng-click will enter the $digest loop.

What is your real use case? If this is for controlling tooltips / popovers pragmatically this should be solved properly in those directives. Using custom triggers to open / close tooltips is a pure hack...

@sudhakar
Copy link

@pkozlowski-opensource Makes perfect sense & I have started refactoring the directive to handle it.

Basically I am in the process of building a directive for providing "App tour" kind of functionality similar to http://usablica.github.io/intro.js/ and http://ryanfunduk.com/jquery-tourbus/. I started with using $tooltip provider & had a service to trigger popovers in sequence. In the end realised that it too hacky & have started using $position provider directly.

Thanks again for making BS modular !!

@chrisirhc
Copy link
Contributor

It looks like this is caused because Firefox fires a mouseout event when the confirm dialog is shown. This happens when the JS thread is waiting for confirm dialog to return and as such it attempts to run the $apply when $apply is already running. I'm not sure if we can get around this through using $evalAsync instead of $apply.

@ersimont
Copy link

ersimont commented Jul 7, 2014

This also happens in Chrome if there are two popups in a row:

http://plnkr.co/edit/rp8iccwoQf16FYp7w4yT?p=preview

@chrisirhc
Copy link
Contributor

It looks like the mouseout event can fire during while click handler is still running. This sounds kind crazy to me since I always thought each mouse event would be independent (one event would run only after the next event finished), but here's the output from the developer tools:
screen shot 2014-07-10 at 12 39 47 am
screen shot 2014-07-10 at 12 42 29 am

It looks like the invocation of a confirm dialog can trigger a mouseout event if it occludes the cursor from the element.

Looks like we'll need a $$phase check in the hideTooltipBind method, as there's really no other way we'll know if a digest cycle is already in progress.

@TroelsL
Copy link

TroelsL commented Feb 17, 2015

I ran into this issue on IE11 (11.0.9600.16428), but was able to monkey patch my way around it with:

function hideTooltipBind() {
  //PATCH for MNSTD-31925
  if(scope.$$phase === "$digest" || scope.$$phase === "$apply") {
    return;
  }
  scope.$apply(function () {
    hide();
  });
}

@gufigueiredo
Copy link

Another scenario:

If i use tooltip with "tooltip-trigger=focus" and ui-datepicker, same error occurs:

                    <div class="form-group">
                        <label for="DateField" class="required">Date Field</label>
                            <div class="input-group date">
                              <input name="DateField" type="text" class="form-control" 
                                     tooltip="Choose a date"
                                     tooltip-trigger="focus"
                                     datepicker-popup="dd/MM/yyyy" 
                                     ng-model="vm.DateField" 
                                     is-open="vm.opened" 
                                     show-button-bar="false" />
                              <span class="input-group-btn">
                                <button type="button" class="btn btn-default" ng-click="vm.open($event)"><i class="glyphicon glyphicon-calendar"></i></button>
                              </span>
                            </div>
                        </div>

@chrisirhc
Copy link
Contributor

@gufigueiredo , that might be a possible use case to support after we release 0.13 . I've opened another issue for that (#3557).

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

7 participants