Skip to content

Commit

Permalink
fix(multiple): move afterRender calls back outside zone
Browse files Browse the repository at this point in the history
Updates some calls to afterRender / afterNextRender to explicitly run
outside the NgZone. These usages were originally explcitily run outside,
but the explicit call was removed when they were updated to use the
afterRender / afterNextRender function, since at the time it was
automatically outside the zone. The semantics of afterRender /
afterNextRendef have since changed so this is no longer true. Therefore
this PR restores the original explicit run outside.
  • Loading branch information
mmalerba committed Apr 30, 2024
1 parent dde3793 commit 92f1534
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 72 deletions.
54 changes: 28 additions & 26 deletions src/cdk/drag-drop/directives/drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,51 +7,51 @@
*/

import {Directionality} from '@angular/cdk/bidi';
import {coerceElement, coerceNumberProperty} from '@angular/cdk/coercion';
import {DOCUMENT} from '@angular/common';
import {
AfterViewInit,
ChangeDetectorRef,
Directive,
ElementRef,
EventEmitter,
Inject,
InjectionToken,
Injector,
Input,
NgZone,
OnChanges,
OnDestroy,
Optional,
Output,
Self,
SimpleChanges,
SkipSelf,
ViewContainerRef,
OnChanges,
SimpleChanges,
ChangeDetectorRef,
Self,
InjectionToken,
booleanAttribute,
afterNextRender,
AfterViewInit,
booleanAttribute,
inject,
Injector,
} from '@angular/core';
import {coerceElement, coerceNumberProperty} from '@angular/cdk/coercion';
import {BehaviorSubject, Observable, Observer, Subject, merge} from 'rxjs';
import {startWith, take, map, takeUntil, switchMap, tap} from 'rxjs/operators';
import {map, startWith, switchMap, take, takeUntil, tap} from 'rxjs/operators';
import {DragDrop} from '../drag-drop';
import type {
CdkDragDrop,
CdkDragEnd,
CdkDragEnter,
CdkDragExit,
CdkDragMove,
CdkDragStart,
CdkDragRelease,
CdkDragStart,
} from '../drag-events';
import {CDK_DRAG_PARENT} from '../drag-parent';
import {DragRef, Point, PreviewContainer} from '../drag-ref';
import {assertElementNode} from './assertions';
import {CDK_DRAG_CONFIG, DragAxis, DragDropConfig, DragStartDelay} from './config';
import {CDK_DRAG_HANDLE, CdkDragHandle} from './drag-handle';
import {CdkDragPlaceholder} from './drag-placeholder';
import {CdkDragPreview} from './drag-preview';
import {CDK_DRAG_PARENT} from '../drag-parent';
import {DragRef, Point, PreviewContainer} from '../drag-ref';
import type {CdkDropList} from './drop-list';
import {DragDrop} from '../drag-drop';
import {CDK_DRAG_CONFIG, DragDropConfig, DragStartDelay, DragAxis} from './config';
import {assertElementNode} from './assertions';

const DRAG_HOST_CLASS = 'cdk-drag';

Expand Down Expand Up @@ -305,16 +305,18 @@ export class CdkDrag<T = any> implements AfterViewInit, OnChanges, OnDestroy {
// element to be in the proper place in the DOM. This is mostly relevant
// for draggable elements inside portals since they get stamped out in
// their original DOM position, and then they get transferred to the portal.
afterNextRender(
() => {
this._updateRootElement();
this._setupHandlesListener();

if (this.freeDragPosition) {
this._dragRef.setFreeDragPosition(this.freeDragPosition);
}
},
{injector: this._injector},
this._ngZone.runOutsideAngular(() =>
afterNextRender(
() => {
this._updateRootElement();
this._setupHandlesListener();

if (this.freeDragPosition) {
this._dragRef.setFreeDragPosition(this.freeDragPosition);
}
},
{injector: this._injector},
),
);
}

Expand Down
17 changes: 9 additions & 8 deletions src/cdk/overlay/overlay-ref.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@
*/

import {Direction, Directionality} from '@angular/cdk/bidi';
import {coerceArray, coerceCssPixelValue} from '@angular/cdk/coercion';
import {ComponentPortal, Portal, PortalOutlet, TemplatePortal} from '@angular/cdk/portal';
import {Location} from '@angular/common';
import {
AfterRenderRef,
ComponentRef,
EmbeddedViewRef,
EnvironmentInjector,
NgZone,
afterNextRender,
afterRender,
untracked,
AfterRenderRef,
} from '@angular/core';
import {Location} from '@angular/common';
import {Observable, Subject, merge, SubscriptionLike, Subscription} from 'rxjs';
import {Observable, Subject, Subscription, SubscriptionLike, merge} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {OverlayKeyboardDispatcher} from './dispatchers/overlay-keyboard-dispatcher';
import {OverlayOutsideClickDispatcher} from './dispatchers/overlay-outside-click-dispatcher';
import {OverlayConfig} from './overlay-config';
import {coerceCssPixelValue, coerceArray} from '@angular/cdk/coercion';
import {PositionStrategy} from './position/position-strategy';
import {ScrollStrategy} from './scroll';

Expand Down Expand Up @@ -495,10 +495,11 @@ export class OverlayRef implements PortalOutlet {
// Run this outside the Angular zone because there's nothing that Angular cares about.
// If it were to run inside the Angular zone, every test that used Overlay would have to be
// either async or fakeAsync.
this._backdropTimeout = this._ngZone.runOutsideAngular(() =>
setTimeout(() => {
this._disposeBackdrop(backdropToDetach);
}, 500),
this._backdropTimeout = this._ngZone.runOutsideAngular(
() =>
setTimeout(() => {
this._disposeBackdrop(backdropToDetach);
}, 500) as any,
);
}

Expand Down
48 changes: 25 additions & 23 deletions src/material/datepicker/calendar-body.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,24 @@
*/

import {Platform, normalizePassiveListenerOptions} from '@angular/cdk/platform';
import {NgClass} from '@angular/common';
import {
AfterViewChecked,
ChangeDetectionStrategy,
Component,
ElementRef,
EventEmitter,
Injector,
Input,
Output,
ViewEncapsulation,
NgZone,
OnChanges,
SimpleChanges,
OnDestroy,
AfterViewChecked,
inject,
Output,
SimpleChanges,
ViewEncapsulation,
afterNextRender,
Injector,
inject,
} from '@angular/core';
import {NgClass} from '@angular/common';

/** Extra CSS classes that can be associated with a calendar cell. */
export type MatCalendarCellCssClasses = string | string[] | Set<string> | {[key: string]: any};
Expand Down Expand Up @@ -312,23 +312,25 @@ export class MatCalendarBody<D = any> implements OnChanges, OnDestroy, AfterView
* Adding delay also complicates writing tests.
*/
_focusActiveCell(movePreview = true) {
afterNextRender(
() => {
setTimeout(() => {
const activeCell: HTMLElement | null = this._elementRef.nativeElement.querySelector(
'.mat-calendar-body-active',
);

if (activeCell) {
if (!movePreview) {
this._skipNextFocus = true;
this._ngZone.runOutsideAngular(() =>
afterNextRender(
() => {
setTimeout(() => {
const activeCell: HTMLElement | null = this._elementRef.nativeElement.querySelector(
'.mat-calendar-body-active',
);

if (activeCell) {
if (!movePreview) {
this._skipNextFocus = true;
}

activeCell.focus();
}

activeCell.focus();
}
});
},
{injector: this._injector},
});
},
{injector: this._injector},
),
);
}

Expand Down
34 changes: 19 additions & 15 deletions src/material/form-field/form-field.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/
import {Directionality} from '@angular/cdk/bidi';
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
import {Platform} from '@angular/cdk/platform';
import {DOCUMENT, NgTemplateOutlet} from '@angular/common';
import {
ANIMATION_MODULE_TYPE,
AfterContentChecked,
AfterContentInit,
AfterViewInit,
Expand All @@ -20,20 +23,19 @@ import {
Inject,
InjectionToken,
Injector,
inject,
Input,
NgZone,
OnDestroy,
Optional,
QueryList,
ViewChild,
ViewEncapsulation,
ANIMATION_MODULE_TYPE,
afterRender,
inject,
} from '@angular/core';
import {AbstractControlDirective} from '@angular/forms';
import {ThemePalette} from '@angular/material/core';
import {merge, Subject} from 'rxjs';
import {Subject, merge} from 'rxjs';
import {takeUntil} from 'rxjs/operators';
import {MAT_ERROR, MatError} from './directives/error';
import {
Expand All @@ -47,14 +49,12 @@ import {MatFormFieldLineRipple} from './directives/line-ripple';
import {MatFormFieldNotchedOutline} from './directives/notched-outline';
import {MAT_PREFIX, MatPrefix} from './directives/prefix';
import {MAT_SUFFIX, MatSuffix} from './directives/suffix';
import {BooleanInput, coerceBooleanProperty} from '@angular/cdk/coercion';
import {matFormFieldAnimations} from './form-field-animations';
import {MatFormFieldControl as _MatFormFieldControl} from './form-field-control';
import {
getMatFormFieldDuplicatedHintError,
getMatFormFieldMissingControlError,
} from './form-field-errors';
import {DOCUMENT, NgTemplateOutlet} from '@angular/common';

/** Type for the available floatLabel values. */
export type FloatLabelType = 'always' | 'auto';
Expand Down Expand Up @@ -307,6 +307,8 @@ export class MatFormField

private _injector = inject(Injector);

private _ngZone = inject(NgZone);

constructor(
public _elementRef: ElementRef,
private _changeDetectorRef: ChangeDetectorRef,
Expand Down Expand Up @@ -503,16 +505,18 @@ export class MatFormField

// TODO(mmalerba): Split this into separate `afterRender` calls using the `EarlyRead` and
// `Write` phases.
afterRender(
() => {
if (this._needsOutlineLabelOffsetUpdate) {
this._needsOutlineLabelOffsetUpdate = false;
this._updateOutlineLabelOffset();
}
},
{
injector: this._injector,
},
this._ngZone.runOutsideAngular(() =>
afterRender(
() => {
if (this._needsOutlineLabelOffsetUpdate) {
this._needsOutlineLabelOffsetUpdate = false;
this._updateOutlineLabelOffset();
}
},
{
injector: this._injector,
},
),
);

this._dir.change
Expand Down

0 comments on commit 92f1534

Please sign in to comment.