Skip to content

Commit 9b86237

Browse files
committed
refactor: replace first operator usages
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.
1 parent dbd64b6 commit 9b86237

File tree

16 files changed

+36
-35
lines changed

16 files changed

+36
-35
lines changed

src/cdk/a11y/focus-trap.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
} from '@angular/core';
1818
import {coerceBooleanProperty} from '@angular/cdk/coercion';
1919
import {Platform} from '@angular/cdk/platform';
20-
import {first} from 'rxjs/operators/first';
20+
import {take} from 'rxjs/operators/take';
2121
import {InteractivityChecker} from './interactivity-checker';
2222

2323

@@ -283,7 +283,7 @@ export class FocusTrap {
283283
if (this._ngZone.isStable) {
284284
fn();
285285
} else {
286-
this._ngZone.onStable.asObservable().pipe(first()).subscribe(fn);
286+
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(fn);
287287
}
288288
}
289289
}

src/cdk/a11y/list-key-manager.spec.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {DOWN_ARROW, TAB, UP_ARROW} from '@angular/cdk/keycodes';
2-
import {first} from 'rxjs/operators/first';
2+
import {take} from 'rxjs/operators/take';
33
import {QueryList} from '@angular/core';
44
import {fakeAsync, tick} from '@angular/core/testing';
55
import {createKeyboardEvent} from '../testing/event-objects';
@@ -196,7 +196,7 @@ describe('Key managers', () => {
196196

197197
it('should emit tabOut when the tab key is pressed', () => {
198198
let spy = jasmine.createSpy('tabOut spy');
199-
keyManager.tabOut.pipe(first()).subscribe(spy);
199+
keyManager.tabOut.pipe(take(1)).subscribe(spy);
200200
keyManager.onKeydown(fakeKeyEvents.tab);
201201

202202
expect(spy).toHaveBeenCalled();

src/cdk/overlay/overlay-ref.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {OverlayConfig} from './overlay-config';
1212
import {OverlayKeyboardDispatcher} from './keyboard/overlay-keyboard-dispatcher';
1313
import {Observable} from 'rxjs/Observable';
1414
import {Subject} from 'rxjs/Subject';
15-
import {first} from 'rxjs/operators/first';
15+
import {take} from 'rxjs/operators/take';
1616

1717

1818
/**
@@ -69,7 +69,7 @@ export class OverlayRef implements PortalOutlet {
6969
// Update the position once the zone is stable so that the overlay will be fully rendered
7070
// before attempting to position it, as the position may depend on the size of the rendered
7171
// content.
72-
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
72+
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
7373
this.updatePosition();
7474
});
7575

src/lib/autocomplete/autocomplete-trigger.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
} from '@angular/cdk/overlay';
1919
import {TemplatePortal} from '@angular/cdk/portal';
2020
import {filter} from 'rxjs/operators/filter';
21-
import {first} from 'rxjs/operators/first';
21+
import {take} from 'rxjs/operators/take';
2222
import {switchMap} from 'rxjs/operators/switchMap';
2323
import {tap} from 'rxjs/operators/tap';
2424
import {delay} from 'rxjs/operators/delay';
@@ -368,7 +368,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
368368
* stream every time the option list changes.
369369
*/
370370
private _subscribeToClosingActions(): Subscription {
371-
const firstStable = this._zone.onStable.asObservable().pipe(first());
371+
const firstStable = this._zone.onStable.asObservable().pipe(take(1));
372372
const optionChanges = this.autocomplete.options.changes.pipe(
373373
tap(() => this._positionStrategy.recalculateLastPosition()),
374374
// Defer emitting to the stream until the next tick, because changing
@@ -387,7 +387,7 @@ export class MatAutocompleteTrigger implements ControlValueAccessor, OnDestroy {
387387
return this.panelClosingActions;
388388
}),
389389
// when the first closing event occurs...
390-
first()
390+
take(1)
391391
)
392392
// set the value, close the panel, and complete.
393393
.subscribe(event => this._setValueAndClose(event));

src/lib/datepicker/calendar.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import {
3636
SimpleChanges,
3737
} from '@angular/core';
3838
import {DateAdapter, MAT_DATE_FORMATS, MatDateFormats} from '@angular/material/core';
39-
import {first} from 'rxjs/operators/first';
39+
import {take} from 'rxjs/operators/take';
4040
import {Subscription} from 'rxjs/Subscription';
4141
import {createMissingDateImplError} from './datepicker-errors';
4242
import {MatDatepickerIntl} from './datepicker-intl';
@@ -261,7 +261,7 @@ export class MatCalendar<D> implements AfterContentInit, OnDestroy, OnChanges {
261261
/** Focuses the active cell after the microtask queue is empty. */
262262
_focusActiveCell() {
263263
this._ngZone.runOutsideAngular(() => {
264-
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
264+
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
265265
this._elementRef.nativeElement.querySelector('.mat-calendar-body-active').focus();
266266
});
267267
});

src/lib/datepicker/datepicker.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import {
1818
ScrollStrategy,
1919
} from '@angular/cdk/overlay';
2020
import {ComponentPortal} from '@angular/cdk/portal';
21-
import {first} from 'rxjs/operators/first';
21+
import {take} from 'rxjs/operators/take';
2222
import {
2323
AfterContentInit,
2424
ChangeDetectionStrategy,
@@ -338,7 +338,7 @@ export class MatDatepicker<D> implements OnDestroy {
338338
componentRef.instance.datepicker = this;
339339

340340
// Update the position once the calendar has rendered.
341-
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
341+
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
342342
this._popupRef.updatePosition();
343343
});
344344
}

src/lib/dialog/dialog-ref.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {OverlayRef, GlobalPositionStrategy} from '@angular/cdk/overlay';
1010
import {filter} from 'rxjs/operators/filter';
11-
import {first} from 'rxjs/operators/first';
11+
import {take} from 'rxjs/operators/take';
1212
import {DialogPosition} from './dialog-config';
1313
import {Observable} from 'rxjs/Observable';
1414
import {Subject} from 'rxjs/Subject';
@@ -50,7 +50,7 @@ export class MatDialogRef<T> {
5050
// Emit when opening animation completes
5151
_containerInstance._animationStateChanged.pipe(
5252
filter(event => event.phaseName === 'done' && event.toState === 'enter'),
53-
first()
53+
take(1)
5454
)
5555
.subscribe(() => {
5656
this._afterOpen.next();
@@ -60,7 +60,7 @@ export class MatDialogRef<T> {
6060
// Dispose overlay when closing animation is complete
6161
_containerInstance._animationStateChanged.pipe(
6262
filter(event => event.phaseName === 'done' && event.toState === 'exit'),
63-
first()
63+
take(1)
6464
)
6565
.subscribe(() => {
6666
this._overlayRef.dispose();
@@ -80,7 +80,7 @@ export class MatDialogRef<T> {
8080
// Transition the backdrop in parallel to the dialog.
8181
this._containerInstance._animationStateChanged.pipe(
8282
filter(event => event.phaseName === 'start'),
83-
first()
83+
take(1)
8484
)
8585
.subscribe(() => {
8686
this._beforeClose.next(dialogResult);

src/lib/form-field/form-field.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88

99
import {animate, state, style, transition, trigger} from '@angular/animations';
1010
import {coerceBooleanProperty} from '@angular/cdk/coercion';
11-
import {first} from 'rxjs/operators/first';
11+
import {take} from 'rxjs/operators/take';
1212
import {startWith} from 'rxjs/operators/startWith';
1313
import {
1414
AfterContentChecked,
@@ -237,7 +237,7 @@ export class MatFormField implements AfterViewInit, AfterContentInit, AfterConte
237237
this._showAlwaysAnimate = true;
238238
this._floatPlaceholder = 'always';
239239

240-
fromEvent(this._placeholder.nativeElement, 'transitionend').pipe(first()).subscribe(() => {
240+
fromEvent(this._placeholder.nativeElement, 'transitionend').pipe(take(1)).subscribe(() => {
241241
this._showAlwaysAnimate = false;
242242
});
243243

src/lib/icon/icon.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
* found in the LICENSE file at https://angular.io/license
77
*/
88

9-
import {first} from 'rxjs/operators/first';
9+
import {take} from 'rxjs/operators/take';
1010
import {
1111
Attribute,
1212
ChangeDetectionStrategy,
@@ -132,7 +132,7 @@ export class MatIcon extends _MatIconMixinBase implements OnChanges, OnInit, Can
132132
if (this.svgIcon) {
133133
const [namespace, iconName] = this._splitIconName(this.svgIcon);
134134

135-
this._iconRegistry.getNamedSvgIcon(iconName, namespace).pipe(first()).subscribe(
135+
this._iconRegistry.getNamedSvgIcon(iconName, namespace).pipe(take(1)).subscribe(
136136
svg => this._setSvgElement(svg),
137137
(err: Error) => console.log(`Error retrieving icon: ${err.message}`)
138138
);

src/lib/menu/menu-directive.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import {Direction} from '@angular/cdk/bidi';
1212
import {ESCAPE, LEFT_ARROW, RIGHT_ARROW} from '@angular/cdk/keycodes';
1313
import {startWith} from 'rxjs/operators/startWith';
1414
import {switchMap} from 'rxjs/operators/switchMap';
15-
import {first} from 'rxjs/operators/first';
15+
import {take} from 'rxjs/operators/take';
1616
import {
1717
AfterContentInit,
1818
ChangeDetectionStrategy,
@@ -189,7 +189,7 @@ export class MatMenu implements AfterContentInit, MatMenuPanel, OnDestroy {
189189

190190
return this._ngZone.onStable
191191
.asObservable()
192-
.pipe(first(), switchMap(() => this._hovered()));
192+
.pipe(take(1), switchMap(() => this._hovered()));
193193
}
194194

195195
/** Handle a keyboard event from the menu, delegating to the appropriate action. */

src/lib/select/select.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
ViewportRuler,
2020
} from '@angular/cdk/overlay';
2121
import {filter} from 'rxjs/operators/filter';
22-
import {first} from 'rxjs/operators/first';
22+
import {take} from 'rxjs/operators/take';
2323
import {map} from 'rxjs/operators/map';
2424
import {startWith} from 'rxjs/operators/startWith';
2525
import {takeUntil} from 'rxjs/operators/takeUntil';
@@ -530,7 +530,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
530530
this._changeDetectorRef.markForCheck();
531531

532532
// Set the font size on the panel element once it exists.
533-
this._ngZone.onStable.asObservable().pipe(first()).subscribe(() => {
533+
this._ngZone.onStable.asObservable().pipe(take(1)).subscribe(() => {
534534
if (this._triggerFontSize && this.overlayDir.overlayRef &&
535535
this.overlayDir.overlayRef.overlayElement) {
536536
this.overlayDir.overlayRef.overlayElement.style.fontSize = `${this._triggerFontSize}px`;
@@ -714,7 +714,7 @@ export class MatSelect extends _MatSelectMixinBase implements AfterContentInit,
714714
* Callback that is invoked when the overlay panel has been attached.
715715
*/
716716
_onAttached(): void {
717-
this.overlayDir.positionChange.pipe(first()).subscribe(() => {
717+
this.overlayDir.positionChange.pipe(take(1)).subscribe(() => {
718718
this._changeDetectorRef.detectChanges();
719719
this._calculateOverlayOffsetX();
720720
this.panel.nativeElement.scrollTop = this._scrollTop;

src/lib/sidenav/drawer.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import {
3333
import {DOCUMENT} from '@angular/platform-browser';
3434
import {merge} from 'rxjs/observable/merge';
3535
import {filter} from 'rxjs/operators/filter';
36-
import {first} from 'rxjs/operators/first';
36+
import {take} from 'rxjs/operators/take';
3737
import {startWith} from 'rxjs/operators/startWith';
3838
import {takeUntil} from 'rxjs/operators/takeUntil';
3939
import {map} from 'rxjs/operators/map';
@@ -346,7 +346,7 @@ export class MatDrawer implements AfterContentInit, OnDestroy {
346346
// TODO(crisbeto): This promise is here for backwards-compatibility.
347347
// It should be removed next time we do breaking changes in the drawer.
348348
return new Promise<any>(resolve => {
349-
this.openedChange.pipe(first()).subscribe(open => {
349+
this.openedChange.pipe(take(1)).subscribe(open => {
350350
resolve(new MatDrawerToggleResult(open ? 'open' : 'close', true));
351351
});
352352
});
@@ -517,7 +517,7 @@ export class MatDrawerContainer implements AfterContentInit, OnDestroy {
517517
// NOTE: We need to wait for the microtask queue to be empty before validating,
518518
// since both drawers may be swapping positions at the same time.
519519
drawer.onPositionChanged.pipe(takeUntil(this._drawers.changes)).subscribe(() => {
520-
this._ngZone.onMicrotaskEmpty.asObservable().pipe(first()).subscribe(() => {
520+
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
521521
this._validateDrawers();
522522
});
523523
});

src/lib/snack-bar/snack-bar-container.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ import {
3232
ComponentPortal,
3333
CdkPortalOutlet,
3434
} from '@angular/cdk/portal';
35-
import {first} from 'rxjs/operators/first';
35+
import {take} from 'rxjs/operators/take';
3636
import {AnimationCurves, AnimationDurations} from '@angular/material/core';
3737
import {Observable} from 'rxjs/Observable';
3838
import {Subject} from 'rxjs/Subject';
@@ -177,7 +177,7 @@ export class MatSnackBarContainer extends BasePortalOutlet implements OnDestroy
177177
* errors where we end up removing an element which is in the middle of an animation.
178178
*/
179179
private _completeExit() {
180-
this._ngZone.onMicrotaskEmpty.asObservable().pipe(first()).subscribe(() => {
180+
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
181181
this._onExit.next();
182182
this._onExit.complete();
183183
});

src/lib/snack-bar/snack-bar.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {MatSnackBarContainer} from './snack-bar-container';
1717
import {MatSnackBarRef} from './snack-bar-ref';
1818
import {BreakpointObserver, Breakpoints} from '@angular/cdk/layout';
1919
import {takeUntil} from 'rxjs/operators/takeUntil';
20-
import {first} from 'rxjs/operators/first';
20+
import {take} from 'rxjs/operators/take';
2121

2222

2323
/**
@@ -153,7 +153,7 @@ export class MatSnackBar {
153153
// appropriate. This class is applied to the overlay element because the overlay must expand to
154154
// fill the width of the screen for full width snackbars.
155155
this._breakpointObserver.observe(Breakpoints.Handset).pipe(
156-
takeUntil(overlayRef.detachments().pipe(first()))
156+
takeUntil(overlayRef.detachments().pipe(take(1)))
157157
).subscribe(state => {
158158
if (state.matches) {
159159
overlayRef.overlayElement.classList.add('mat-snack-bar-handset');

src/lib/tooltip/tooltip.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ import {
2424
} from '@angular/cdk/overlay';
2525
import {Platform} from '@angular/cdk/platform';
2626
import {ComponentPortal} from '@angular/cdk/portal';
27-
import {first} from 'rxjs/operators/first';
27+
import {take} from 'rxjs/operators/take';
2828
import {ScrollDispatcher} from '@angular/cdk/scrolling';
2929
import {
3030
ChangeDetectionStrategy,
@@ -387,7 +387,7 @@ export class MatTooltip implements OnDestroy {
387387
this._tooltipInstance.message = this.message;
388388
this._tooltipInstance._markForCheck();
389389

390-
this._ngZone.onMicrotaskEmpty.asObservable().pipe(first()).subscribe(() => {
390+
this._ngZone.onMicrotaskEmpty.asObservable().pipe(take(1)).subscribe(() => {
391391
if (this._tooltipInstance) {
392392
this._overlayRef!.updatePosition();
393393
}

tools/package-tools/rollup-globals.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ export const rollupGlobals = {
7373
'rxjs/operators/debounceTime': 'Rx.Observable',
7474
'rxjs/operators/takeUntil': 'Rx.Observable',
7575
'rxjs/operators/first': 'Rx.Observable',
76+
'rxjs/operators/take': 'Rx.Observable',
7677
'rxjs/operators/filter': 'Rx.Observable',
7778
'rxjs/operators/map': 'Rx.Observable',
7879
'rxjs/operators/tap': 'Rx.Observable',

0 commit comments

Comments
 (0)