Skip to content

Commit

Permalink
fix(tooltip): wrong position when using OnPush change detection
Browse files Browse the repository at this point in the history
Fixes the tooltip having a wrong position if it is inside a component with `OnPush` change detection. The issue was due to the fact that when `OnPush` is used, the tooltip text isn't rendered at the moment when the element is positioned.

Fixes angular#3497.
  • Loading branch information
crisbeto committed Mar 18, 2017
1 parent 3ad6ff0 commit 366166e
Show file tree
Hide file tree
Showing 2 changed files with 25 additions and 6 deletions.
10 changes: 9 additions & 1 deletion src/lib/tooltip/tooltip.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ describe('MdTooltip', () => {

fixture.detectChanges();

// wait till animation has finished
// wait until animation has finished
tick(500);

// Make sure tooltip is shown to the user and animation has finished
Expand All @@ -432,6 +432,14 @@ describe('MdTooltip', () => {
flushMicrotasks();
expect(tooltipDirective._tooltipInstance).toBeNull();
}));

it('should have rendered the tooltip text on load', fakeAsync(() => {
dispatchFakeEvent(buttonElement, 'mouseenter');
tick(0);

const tooltipElement = overlayContainerElement.querySelector('.mat-tooltip') as HTMLElement;
expect(tooltipElement.textContent).toContain('initial tooltip message');
}));
});

describe('destroy', () => {
Expand Down
21 changes: 16 additions & 5 deletions src/lib/tooltip/tooltip.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
OnDestroy,
Renderer,
OnInit,
ChangeDetectorRef
ChangeDetectorRef,
} from '@angular/core';
import {
Overlay,
Expand Down Expand Up @@ -155,7 +155,7 @@ export class MdTooltip implements OnInit, OnDestroy {
@Optional() private _dir: Dir) {

// The mouse events shouldn't be bound on iOS devices, because
// they can prevent the first tap from firing it's click event.
// they can prevent the first tap from firing its click event.
if (!_platform.IOS) {
_renderer.listen(_elementRef.nativeElement, 'mouseenter', () => this.show());
_renderer.listen(_elementRef.nativeElement, 'mouseleave', () => this.hide());
Expand Down Expand Up @@ -311,6 +311,8 @@ export class MdTooltip implements OnInit, OnDestroy {
// Must wait for the message to be painted to the tooltip so that the overlay can properly
// calculate the correct positioning based on the size of the text.
this._tooltipInstance.message = message;
this._tooltipInstance._updateView();

this._ngZone.onMicrotaskEmpty.first().subscribe(() => {
if (this._tooltipInstance) {
this._overlayRef.updatePosition();
Expand Down Expand Up @@ -393,7 +395,7 @@ export class TooltipComponent {
// Mark for check so if any parent component has set the
// ChangeDetectionStrategy to OnPush it will be checked anyways
this._changeDetectorRef.markForCheck();
setTimeout(() => { this._closeOnInteraction = true; }, 0);
setTimeout(() => this._closeOnInteraction = true, 0);
}, delay);
}

Expand Down Expand Up @@ -439,8 +441,8 @@ export class TooltipComponent {
case 'after': this._transformOrigin = isLtr ? 'left' : 'right'; break;
case 'left': this._transformOrigin = 'right'; break;
case 'right': this._transformOrigin = 'left'; break;
case 'above': this._transformOrigin = 'bottom'; break;
case 'below': this._transformOrigin = 'top'; break;
case 'above': this._transformOrigin = 'bottom'; break;
case 'below': this._transformOrigin = 'top'; break;
default: throw new MdTooltipInvalidPositionError(value);
}
}
Expand All @@ -461,4 +463,13 @@ export class TooltipComponent {
this.hide(0);
}
}

/**
* Ensures that the tooltip text has rendered. Mainly used for rendering the initial text
* before positioning a tooltip, which can be problematic in components with OnPush change
* detection.
*/
_updateView(): void {
this._changeDetectorRef.detectChanges();
}
}

0 comments on commit 366166e

Please sign in to comment.