mirror of
https://github.com/angular/angular.git
synced 2026-09-14 13:54:52 +08:00
refactor: replace first operator usages (#8352)
Replaces all usages of the `first` operator with `take(1)` in order to avoid some cryptic errors that `first` throws when the observable completes before it has emitted a value.
This commit is contained in:
committed by
Jeremy Elbourn
parent
2cf3860e59
commit
623be22e58
@@ -17,7 +17,7 @@ import {
|
||||
Inject,
|
||||
} from '@angular/core';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {InteractivityChecker} from './interactivity-checker';
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
|
||||
@@ -271,7 +271,7 @@ export class FocusTrap {
|
||||
if (this._ngZone.isStable) {
|
||||
fn();
|
||||
} else {
|
||||
this._ngZone.onStable.asObservable().pipe(first()).subscribe(fn);
|
||||
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(fn);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import {DOWN_ARROW, TAB, UP_ARROW} from '@angular/cdk/keycodes';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {QueryList} from '@angular/core';
|
||||
import {fakeAsync, tick} from '@angular/core/testing';
|
||||
import {createKeyboardEvent} from '../testing/event-objects';
|
||||
@@ -196,7 +196,7 @@ describe('Key managers', () => {
|
||||
|
||||
it('should emit tabOut when the tab key is pressed', () => {
|
||||
let spy = jasmine.createSpy('tabOut spy');
|
||||
keyManager.tabOut.pipe(first()).subscribe(spy);
|
||||
keyManager.tabOut.pipe(take(1)).subscribe(spy);
|
||||
keyManager.onKeydown(fakeKeyEvents.tab);
|
||||
|
||||
expect(spy).toHaveBeenCalled();
|
||||
|
||||
@@ -12,7 +12,7 @@ import {OverlayConfig} from './overlay-config';
|
||||
import {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
|
||||
|
||||
/**
|
||||
@@ -69,7 +69,7 @@ export class OverlayRef implements PortalOutlet {
|
||||
// Update the position once the zone is stable so that the overlay will be fully rendered
|
||||
// before attempting to position it, as the position may depend on the size of the rendered
|
||||
// content.
|
||||
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
|
||||
this.updatePosition();
|
||||
});
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from '@angular/cdk/overlay';
|
||||
import {TemplatePortal} from '@angular/cdk/portal';
|
||||
import {filter} from 'rxjs/operators/filter';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {switchMap} from 'rxjs/operators/switchMap';
|
||||
import {tap} from 'rxjs/operators/tap';
|
||||
import {delay} from 'rxjs/operators/delay';
|
||||
@@ -368,7 +368,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
* stream every time the option list changes.
|
||||
*/
|
||||
private _subscribeToClosingActions(): Subscription {
|
||||
const firstStable = this._zone.onStable.asObservable().pipe(first());
|
||||
const firstStable = this._zone.onStable.asObservable().pipe(take(1));
|
||||
const optionChanges = this.autocomplete.options.changes.pipe(
|
||||
tap(() => this._positionStrategy.recalculateLastPosition()),
|
||||
// Defer emitting to the stream until the next tick, because changing
|
||||
@@ -387,7 +387,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
|
||||
return this.panelClosingActions;
|
||||
}),
|
||||
// when the first closing event occurs...
|
||||
first()
|
||||
take(1)
|
||||
)
|
||||
// set the value, close the panel, and complete.
|
||||
.subscribe(event => this._setValueAndClose(event));
|
||||
|
||||
@@ -36,7 +36,7 @@ import {
|
||||
SimpleChanges,
|
||||
} from '@angular/core';
|
||||
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {Subscription} from 'rxjs/Subscription';
|
||||
import {createMissingDateImplError} from './datepicker-errors';
|
||||
import {MatDatepickerIntl} from './datepicker-intl';
|
||||
@@ -261,7 +261,7 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
|
||||
/** Focuses the active cell after the microtask queue is empty. */
|
||||
_focusActiveCell() {
|
||||
this._ngZone.runOutsideAngular(() => {
|
||||
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
|
||||
this._elementRef.nativeElement.querySelector('.mat-calendar-body-active').focus();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
ScrollStrategy,
|
||||
} from '@angular/cdk/overlay';
|
||||
import {ComponentPortal} from '@angular/cdk/portal';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {
|
||||
AfterContentInit,
|
||||
ChangeDetectionStrategy,
|
||||
@@ -345,7 +345,7 @@ export class MatDatepicker<D> implements OnDestroy {
|
||||
componentRef.instance.datepicker = this;
|
||||
|
||||
// Update the position once the calendar has rendered.
|
||||
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
|
||||
this._popupRef.updatePosition();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {OverlayRef, GlobalPositionStrategy} from '@angular/cdk/overlay';
|
||||
import {filter} from 'rxjs/operators/filter';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {DialogPosition} from './dialog-config';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
@@ -50,7 +50,7 @@ export class MatDialogRef<T> {
|
||||
// Emit when opening animation completes
|
||||
_containerInstance._animationStateChanged.pipe(
|
||||
filter(event => event.phaseName === 'done' && event.toState === 'enter'),
|
||||
first()
|
||||
take(1)
|
||||
)
|
||||
.subscribe(() => {
|
||||
this._afterOpen.next();
|
||||
@@ -60,7 +60,7 @@ export class MatDialogRef<T> {
|
||||
// Dispose overlay when closing animation is complete
|
||||
_containerInstance._animationStateChanged.pipe(
|
||||
filter(event => event.phaseName === 'done' && event.toState === 'exit'),
|
||||
first()
|
||||
take(1)
|
||||
)
|
||||
.subscribe(() => {
|
||||
this._overlayRef.dispose();
|
||||
@@ -80,7 +80,7 @@ export class MatDialogRef<T> {
|
||||
// Transition the backdrop in parallel to the dialog.
|
||||
this._containerInstance._animationStateChanged.pipe(
|
||||
filter(event => event.phaseName === 'start'),
|
||||
first()
|
||||
take(1)
|
||||
)
|
||||
.subscribe(() => {
|
||||
this._beforeClose.next(dialogResult);
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
import {animate, state, style, transition, trigger} from '@angular/animations';
|
||||
import {coerceBooleanProperty} from '@angular/cdk/coercion';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {startWith} from 'rxjs/operators/startWith';
|
||||
import {
|
||||
AfterContentChecked,
|
||||
@@ -236,7 +236,7 @@ export class MatFormField implements AfterViewInit, AfterContentInit, AfterConte
|
||||
this._showAlwaysAnimate = true;
|
||||
this._floatPlaceholder = 'always';
|
||||
|
||||
fromEvent(this._placeholder.nativeElement, 'transitionend').pipe(first()).subscribe(() => {
|
||||
fromEvent(this._placeholder.nativeElement, 'transitionend').pipe(take(1)).subscribe(() => {
|
||||
this._showAlwaysAnimate = false;
|
||||
});
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {
|
||||
Attribute,
|
||||
ChangeDetectionStrategy,
|
||||
@@ -130,7 +130,7 @@ export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, Can
|
||||
if (this.svgIcon) {
|
||||
const [namespace, iconName] = this._splitIconName(this.svgIcon);
|
||||
|
||||
this._iconRegistry.getNamedSvgIcon(iconName, namespace).pipe(first()).subscribe(
|
||||
this._iconRegistry.getNamedSvgIcon(iconName, namespace).pipe(take(1)).subscribe(
|
||||
svg => this._setSvgElement(svg),
|
||||
(err: Error) => console.log(`Error retrieving icon: ${err.message}`)
|
||||
);
|
||||
|
||||
@@ -12,7 +12,7 @@ import {Direction} from '@angular/cdk/bidi';
|
||||
import {ESCAPE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes';
|
||||
import {startWith} from 'rxjs/operators/startWith';
|
||||
import {switchMap} from 'rxjs/operators/switchMap';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {
|
||||
AfterContentInit,
|
||||
ChangeDetectionStrategy,
|
||||
@@ -202,7 +202,7 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy {
|
||||
|
||||
return this._ngZone.onStable
|
||||
.asObservable()
|
||||
.pipe(first(), switchMap(() => this._hovered()));
|
||||
.pipe(take(1), switchMap(() => this._hovered()));
|
||||
}
|
||||
|
||||
/** Handle a keyboard event from the menu, delegating to the appropriate action. */
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
ViewportRuler,
|
||||
} from '@angular/cdk/overlay';
|
||||
import {filter} from 'rxjs/operators/filter';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {map} from 'rxjs/operators/map';
|
||||
import {startWith} from 'rxjs/operators/startWith';
|
||||
import {takeUntil} from 'rxjs/operators/takeUntil';
|
||||
@@ -528,7 +528,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
|
||||
this._changeDetectorRef.markForCheck();
|
||||
|
||||
// Set the font size on the panel element once it exists.
|
||||
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
|
||||
if (this._triggerFontSize && this.overlayDir.overlayRef &&
|
||||
this.overlayDir.overlayRef.overlayElement) {
|
||||
this.overlayDir.overlayRef.overlayElement.style.fontSize = `${this._triggerFontSize}px`;
|
||||
@@ -712,7 +712,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
|
||||
* Callback that is invoked when the overlay panel has been attached.
|
||||
*/
|
||||
_onAttached(): void {
|
||||
this.overlayDir.positionChange.pipe(first()).subscribe(() => {
|
||||
this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {
|
||||
this._changeDetectorRef.detectChanges();
|
||||
this._calculateOverlayOffsetX();
|
||||
this.panel.nativeElement.scrollTop = this._scrollTop;
|
||||
|
||||
@@ -32,7 +32,7 @@ import {
|
||||
import {DOCUMENT} from '@angular/common';
|
||||
import {merge} from 'rxjs/observable/merge';
|
||||
import {filter} from 'rxjs/operators/filter';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {startWith} from 'rxjs/operators/startWith';
|
||||
import {takeUntil} from 'rxjs/operators/takeUntil';
|
||||
import {map} from 'rxjs/operators/map';
|
||||
@@ -345,7 +345,7 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
|
||||
// TODO(crisbeto): This promise is here for backwards-compatibility.
|
||||
// It should be removed next time we do breaking changes in the drawer.
|
||||
return new Promise<any>(resolve => {
|
||||
this.openedChange.pipe(first()).subscribe(open => {
|
||||
this.openedChange.pipe(take(1)).subscribe(open => {
|
||||
resolve(new MatDrawerToggleResult(open ? 'open' : 'close', true));
|
||||
});
|
||||
});
|
||||
@@ -515,7 +515,7 @@ export class MatDrawerContainer implements AfterContentInit, OnDestroy {
|
||||
// NOTE: We need to wait for the microtask queue to be empty before validating,
|
||||
// since both drawers may be swapping positions at the same time.
|
||||
drawer.onPositionChanged.pipe(takeUntil(this._drawers.changes)).subscribe(() => {
|
||||
this._ngZone.onMicrotaskEmpty.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
|
||||
this._validateDrawers();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -31,7 +31,7 @@ import {
|
||||
ComponentPortal,
|
||||
CdkPortalOutlet,
|
||||
} from '@angular/cdk/portal';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {AnimationCurves, AnimationDurations} from '@angular/material/core';
|
||||
import {Observable} from 'rxjs/Observable';
|
||||
import {Subject} from 'rxjs/Subject';
|
||||
@@ -170,7 +170,7 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
|
||||
* errors where we end up removing an element which is in the middle of an animation.
|
||||
*/
|
||||
private _completeExit() {
|
||||
this._ngZone.onMicrotaskEmpty.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
|
||||
this._onExit.next();
|
||||
this._onExit.complete();
|
||||
});
|
||||
|
||||
@@ -11,7 +11,7 @@ import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
|
||||
import {Overlay, OverlayConfig, OverlayRef} from '@angular/cdk/overlay';
|
||||
import {ComponentPortal, ComponentType, PortalInjector} from '@angular/cdk/portal';
|
||||
import {ComponentRef, Injectable, Injector, Optional, SkipSelf} from '@angular/core';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {takeUntil} from 'rxjs/operators/takeUntil';
|
||||
import {SimpleSnackBar} from './simple-snack-bar';
|
||||
import {MAT_SNACK_BAR_DATA, MatSnackBarConfig} from './snack-bar-config';
|
||||
@@ -152,7 +152,7 @@ export class MatSnackBar {
|
||||
// appropriate. This class is applied to the overlay element because the overlay must expand to
|
||||
// fill the width of the screen for full width snackbars.
|
||||
this._breakpointObserver.observe(Breakpoints.Handset).pipe(
|
||||
takeUntil(overlayRef.detachments().pipe(first()))
|
||||
takeUntil(overlayRef.detachments().pipe(take(1)))
|
||||
).subscribe(state => {
|
||||
if (state.matches) {
|
||||
overlayRef.overlayElement.classList.add('mat-snack-bar-handset');
|
||||
|
||||
@@ -24,7 +24,7 @@ import {
|
||||
} from '@angular/cdk/overlay';
|
||||
import {Platform} from '@angular/cdk/platform';
|
||||
import {ComponentPortal} from '@angular/cdk/portal';
|
||||
import {first} from 'rxjs/operators/first';
|
||||
import {take} from 'rxjs/operators/take';
|
||||
import {merge} from 'rxjs/observable/merge';
|
||||
import {ScrollDispatcher} from '@angular/cdk/scrolling';
|
||||
import {
|
||||
@@ -389,7 +389,7 @@ export class MatTooltip implements OnDestroy {
|
||||
this._tooltipInstance.message = this.message;
|
||||
this._tooltipInstance._markForCheck();
|
||||
|
||||
this._ngZone.onMicrotaskEmpty.asObservable().pipe(first()).subscribe(() => {
|
||||
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
|
||||
if (this._tooltipInstance) {
|
||||
this._overlayRef!.updatePosition();
|
||||
}
|
||||
|
||||
@@ -74,6 +74,7 @@ export const rollupGlobals = {
|
||||
'rxjs/operators/debounceTime': 'Rx.Observable',
|
||||
'rxjs/operators/takeUntil': 'Rx.Observable',
|
||||
'rxjs/operators/first': 'Rx.Observable',
|
||||
'rxjs/operators/take': 'Rx.Observable',
|
||||
'rxjs/operators/filter': 'Rx.Observable',
|
||||
'rxjs/operators/map': 'Rx.Observable',
|
||||
'rxjs/operators/tap': 'Rx.Observable',
|
||||
|
||||
Reference in New Issue
Block a user