From 30445a2360e41ffdb3524c13fc293ee7a102313d Mon Sep 17 00:00:00 2001 From: crisbeto Date: Wed, 21 Jun 2017 21:22:50 +0200 Subject: [PATCH] feat: remove uses of rxjs patch operators Refactors the entire codebase not to use the patch operators from RxJS, because they pollute the user's setup. Instead, opts into importing the operators directly and chaining them via the `RxChain` class. Fixes #2622. --- CODING_STANDARDS.md | 30 +++- .../autocomplete/autocomplete-demo.ts | 1 + src/demo-app/data-table/person-data-source.ts | 1 + src/lib/autocomplete/autocomplete-trigger.ts | 59 ++++--- src/lib/autocomplete/autocomplete.spec.ts | 24 +-- src/lib/core/a11y/focus-trap.ts | 7 +- src/lib/core/a11y/list-key-manager.spec.ts | 3 +- src/lib/core/data-table/data-table.spec.ts | 6 +- src/lib/core/data-table/data-table.ts | 43 +++--- .../core/observe-content/observe-content.ts | 8 +- .../core/overlay/scroll/scroll-dispatcher.ts | 15 +- src/lib/core/overlay/scroll/scrollable.ts | 1 - src/lib/core/rxjs/index.ts | 2 + src/lib/core/rxjs/rx-chain.spec.ts | 41 +++++ src/lib/core/rxjs/rx-chain.ts | 52 +++++++ src/lib/core/rxjs/rx-operators.ts | 144 ++++++++++++++++++ src/lib/core/style/focus-origin-monitor.ts | 5 +- src/lib/datepicker/calendar.ts | 6 +- src/lib/datepicker/datepicker.ts | 4 +- src/lib/dialog/dialog-ref.ts | 6 +- src/lib/icon/icon-registry.ts | 116 +++++++------- src/lib/icon/icon.ts | 3 +- src/lib/select/select.ts | 15 +- src/lib/sidenav/sidenav.ts | 9 +- src/lib/snack-bar/snack-bar-container.ts | 3 +- src/lib/tabs/tab-body.ts | 1 - src/lib/tabs/tab-group.ts | 4 +- src/lib/tabs/tab-header.ts | 18 +-- src/lib/tabs/tab-nav-bar/tab-nav-bar.ts | 18 +-- src/lib/tooltip/tooltip.ts | 4 +- tools/package-tools/rollup-helpers.ts | 40 ++--- 31 files changed, 478 insertions(+), 211 deletions(-) create mode 100644 src/lib/core/rxjs/index.ts create mode 100644 src/lib/core/rxjs/rx-chain.spec.ts create mode 100644 src/lib/core/rxjs/rx-chain.ts create mode 100644 src/lib/core/rxjs/rx-operators.ts diff --git a/CODING_STANDARDS.md b/CODING_STANDARDS.md index 647bc54f7dde..1e37d6f3558f 100644 --- a/CODING_STANDARDS.md +++ b/CODING_STANDARDS.md @@ -124,6 +124,34 @@ class ConfigBuilder { } ``` +#### RxJS +When dealing with RxJS operators, import the operator functions directly (e.g. +`import "rxjs/operator/map"`), as opposed to using the "patch" imports which pollute the user's +global Observable object (e.g. `import "rxjs/add/operator/map"`): + +```ts +// NO +import 'rxjs/add/operator/map'; +someObservable.map(...).subscribe(...); + +// YES +import {map} from 'rxjs/operator/map'; +map.call(someObservable, ...).subscribe(...); +``` + +Note that this approach can be inflexible when dealing with long chains of operators. You can use +the `RxChain` class to help with it: + +```ts +// Before +someObservable.filter(...).map(...).do(...); + +// After +RxChain.from(someObservable).call(filter, ...).call(map, ...).call(do, ...).subscribe(...); +``` +Note that not all operators are available via the `RxChain`. If the operator that you need isn't +declared, you can add it to `/core/rxjs/rx-operators.ts`. + #### Access modifiers * Omit the `public` keyword as it is the default behavior. * Use `private` when appropriate and possible, prefixing the name with an underscore. @@ -279,7 +307,7 @@ This makes it easier to override styles when necessary. For example, rather than ```scss the-host-element { // ... - + .some-child-element { color: red; } diff --git a/src/demo-app/autocomplete/autocomplete-demo.ts b/src/demo-app/autocomplete/autocomplete-demo.ts index 7fa011eeadf9..96c25f70f866 100644 --- a/src/demo-app/autocomplete/autocomplete-demo.ts +++ b/src/demo-app/autocomplete/autocomplete-demo.ts @@ -1,6 +1,7 @@ import {Component, ViewChild, ViewEncapsulation} from '@angular/core'; import {FormControl, NgModel} from '@angular/forms'; import 'rxjs/add/operator/startWith'; +import 'rxjs/add/operator/map'; @Component({ moduleId: module.id, diff --git a/src/demo-app/data-table/person-data-source.ts b/src/demo-app/data-table/person-data-source.ts index 18904b2b3d1d..c7564d05217f 100644 --- a/src/demo-app/data-table/person-data-source.ts +++ b/src/demo-app/data-table/person-data-source.ts @@ -1,6 +1,7 @@ import {CollectionViewer, DataSource} from '@angular/material'; import {Observable} from 'rxjs/Observable'; import {PeopleDatabase, UserData} from './people-database'; +import 'rxjs/add/operator/map'; export class PersonDataSource extends DataSource { _renderedData: any[] = []; diff --git a/src/lib/autocomplete/autocomplete-trigger.ts b/src/lib/autocomplete/autocomplete-trigger.ts index 86cdbfbadd9c..80bc474647c5 100644 --- a/src/lib/autocomplete/autocomplete-trigger.ts +++ b/src/lib/autocomplete/autocomplete-trigger.ts @@ -31,11 +31,10 @@ import {ENTER, UP_ARROW, DOWN_ARROW, ESCAPE} from '../core/keyboard/keycodes'; import {Directionality} from '../core/bidi/index'; import {MdInputContainer} from '../input/input-container'; import {Subscription} from 'rxjs/Subscription'; -import 'rxjs/add/observable/merge'; -import 'rxjs/add/observable/fromEvent'; -import 'rxjs/add/operator/filter'; -import 'rxjs/add/operator/switchMap'; -import 'rxjs/add/observable/of'; +import {merge} from 'rxjs/observable/merge'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {of as observableOf} from 'rxjs/observable/of'; +import {RxChain, switchMap, first, filter} from '../core/rxjs/index'; /** * The following style constants are necessary to save here in order @@ -187,7 +186,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { * when an option is selected, on blur, and when TAB is pressed. */ get panelClosingActions(): Observable { - return Observable.merge( + return merge( this.optionSelections, this.autocomplete._keyManager.tabOut, this._outsideClickStream @@ -196,7 +195,7 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { /** Stream of autocomplete option selections. */ get optionSelections(): Observable { - return Observable.merge(...this.autocomplete.options.map(option => option.onSelectionChange)); + return merge(...this.autocomplete.options.map(option => option.onSelectionChange)); } /** The currently active option, coerced to MdOption type. */ @@ -210,20 +209,20 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { /** Stream of clicks outside of the autocomplete panel. */ private get _outsideClickStream(): Observable { - if (this._document) { - return Observable.fromEvent(this._document, 'click').filter((event: MouseEvent) => { - const clickTarget = event.target as HTMLElement; - const inputContainer = this._inputContainer ? - this._inputContainer._elementRef.nativeElement : null; - - return this._panelOpen && - clickTarget !== this._element.nativeElement && - (!inputContainer || !inputContainer.contains(clickTarget)) && - (!!this._overlayRef && !this._overlayRef.overlayElement.contains(clickTarget)); - }); + if (!this._document) { + return observableOf(null); } - return Observable.of(null); + return filter.call(fromEvent(this._document, 'click'), (event: MouseEvent) => { + const clickTarget = event.target as HTMLElement; + const inputContainer = this._inputContainer ? + this._inputContainer._elementRef.nativeElement : null; + + return this._panelOpen && + clickTarget !== this._element.nativeElement && + (!inputContainer || !inputContainer.contains(clickTarget)) && + (!!this._overlayRef && !this._overlayRef.overlayElement.contains(clickTarget)); + }); } /** @@ -332,17 +331,17 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { */ private _subscribeToClosingActions(): void { // When the zone is stable initially, and when the option list changes... - Observable.merge(this._zone.onStable.first(), this.autocomplete.options.changes) - // create a new stream of panelClosingActions, replacing any previous streams - // that were created, and flatten it so our stream only emits closing events... - .switchMap(() => { - this._resetPanel(); - return this.panelClosingActions; - }) - // when the first closing event occurs... - .first() - // set the value, close the panel, and complete. - .subscribe(event => this._setValueAndClose(event)); + RxChain.from(merge(first.call(this._zone.onStable), this.autocomplete.options.changes)) + // create a new stream of panelClosingActions, replacing any previous streams + // that were created, and flatten it so our stream only emits closing events... + .call(switchMap, () => { + this._resetPanel(); + return this.panelClosingActions; + }) + // when the first closing event occurs... + .call(first) + // set the value, close the panel, and complete. + .subscribe(event => this._setValueAndClose(event)); } /** Destroys the autocomplete suggestion panel. */ diff --git a/src/lib/autocomplete/autocomplete.spec.ts b/src/lib/autocomplete/autocomplete.spec.ts index 422456f40bd4..aa1e29fd05dc 100644 --- a/src/lib/autocomplete/autocomplete.spec.ts +++ b/src/lib/autocomplete/autocomplete.spec.ts @@ -30,7 +30,7 @@ import {dispatchFakeEvent} from '../core/testing/dispatch-events'; import {createKeyboardEvent} from '../core/testing/event-objects'; import {typeInElement} from '../core/testing/type-in-element'; import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher'; -import 'rxjs/add/operator/map'; +import {RxChain, map, startWith, filter} from '../core/rxjs/index'; describe('MdAutocomplete', () => { @@ -1321,10 +1321,13 @@ class NgIfAutocomplete { @ViewChildren(MdOption) mdOptions: QueryList; constructor() { - this.filteredOptions = this.optionCtrl.valueChanges.startWith(null).map((val) => { - return val ? this.options.filter(option => new RegExp(val, 'gi').test(option)) - : this.options.slice(); - }); + this.filteredOptions = RxChain.from(this.optionCtrl.valueChanges) + .call(startWith, null) + .call(map, (val: string) => { + return val ? this.options.filter(option => new RegExp(val, 'gi').test(option)) + : this.options.slice(); + }) + .result(); } } @@ -1430,10 +1433,13 @@ class AutocompleteWithNativeInput { @ViewChildren(MdOption) mdOptions: QueryList; constructor() { - this.filteredOptions = this.optionCtrl.valueChanges.startWith(null).map((val) => { - return val ? this.options.filter(option => new RegExp(val, 'gi').test(option)) - : this.options.slice(); - }); + this.filteredOptions = RxChain.from(this.optionCtrl.valueChanges) + .call(startWith, null) + .call(map, (val: string) => { + return val ? this.options.filter(option => new RegExp(val, 'gi').test(option)) + : this.options.slice(); + }) + .result(); } } diff --git a/src/lib/core/a11y/focus-trap.ts b/src/lib/core/a11y/focus-trap.ts index 6206fb724ea7..10510b335d17 100644 --- a/src/lib/core/a11y/focus-trap.ts +++ b/src/lib/core/a11y/focus-trap.ts @@ -15,11 +15,10 @@ import { AfterContentInit, Injectable, } from '@angular/core'; +import {coerceBooleanProperty} from '@angular/cdk'; import {InteractivityChecker} from './interactivity-checker'; import {Platform} from '../platform/platform'; -import {coerceBooleanProperty} from '@angular/cdk'; - -import 'rxjs/add/operator/first'; +import {first} from '../rxjs/index'; /** @@ -232,7 +231,7 @@ export class FocusTrap { if (this._ngZone.isStable) { fn(); } else { - this._ngZone.onStable.first().subscribe(fn); + first.call(this._ngZone.onStable).subscribe(fn); } } } diff --git a/src/lib/core/a11y/list-key-manager.spec.ts b/src/lib/core/a11y/list-key-manager.spec.ts index 6ee209051e18..29e887a02b21 100644 --- a/src/lib/core/a11y/list-key-manager.spec.ts +++ b/src/lib/core/a11y/list-key-manager.spec.ts @@ -5,6 +5,7 @@ import {DOWN_ARROW, UP_ARROW, TAB} from '../keyboard/keycodes'; import {ListKeyManager} from './list-key-manager'; import {ActiveDescendantKeyManager} from './activedescendant-key-manager'; import {createKeyboardEvent} from '../testing/event-objects'; +import {first} from '../rxjs/index'; class FakeFocusable { @@ -189,7 +190,7 @@ describe('Key managers', () => { it('should emit tabOut when the tab key is pressed', () => { let spy = jasmine.createSpy('tabOut spy'); - keyManager.tabOut.first().subscribe(spy); + first.call(keyManager.tabOut).subscribe(spy); keyManager.onKeydown(fakeKeyEvents.tab); expect(spy).toHaveBeenCalled(); diff --git a/src/lib/core/data-table/data-table.spec.ts b/src/lib/core/data-table/data-table.spec.ts index 38ed58d44bb6..eee249699e92 100644 --- a/src/lib/core/data-table/data-table.spec.ts +++ b/src/lib/core/data-table/data-table.spec.ts @@ -2,9 +2,11 @@ import {async, ComponentFixture, TestBed} from '@angular/core/testing'; import {Component, ViewChild} from '@angular/core'; import {CdkTable} from './data-table'; import {CollectionViewer, DataSource} from './data-source'; -import {Observable} from 'rxjs/Observable'; import {BehaviorSubject} from 'rxjs/BehaviorSubject'; import {customMatchers} from '../testing/jasmine-matchers'; +import {Observable} from 'rxjs/Observable'; +import {combineLatest} from 'rxjs/observable/combineLatest'; +import {map} from '../rxjs/index'; import {CdkDataTableModule} from './index'; describe('CdkTable', () => { @@ -247,7 +249,7 @@ class FakeDataSource extends DataSource { connect(collectionViewer: CollectionViewer): Observable { this.isConnected = true; const streams = [this._dataChange, collectionViewer.viewChange]; - return Observable.combineLatest(streams).map(([data]) => data); + return map.call(combineLatest(streams), ([data]) => data); } addData() { diff --git a/src/lib/core/data-table/data-table.ts b/src/lib/core/data-table/data-table.ts index f8f7cab1caed..5c288694f634 100644 --- a/src/lib/core/data-table/data-table.ts +++ b/src/lib/core/data-table/data-table.ts @@ -29,11 +29,9 @@ import { import {CollectionViewer, DataSource} from './data-source'; import {BaseRowDef, CdkCellOutlet, CdkHeaderRowDef, CdkRowDef} from './row'; import {CdkCellDef, CdkColumnDef, CdkHeaderCellDef} from './cell'; -import {Observable} from 'rxjs/Observable'; +import {merge} from 'rxjs/observable/merge'; +import {takeUntil} from '../rxjs/index'; import {BehaviorSubject} from 'rxjs/BehaviorSubject'; -import 'rxjs/add/operator/let'; -import 'rxjs/add/operator/debounceTime'; -import 'rxjs/add/observable/combineLatest'; import {Subscription} from 'rxjs/Subscription'; import {Subject} from 'rxjs/Subject'; @@ -179,22 +177,20 @@ export class CdkTable implements CollectionViewer { // Re-render the rows if any of their columns change. // TODO(andrewseguin): Determine how to only re-render the rows that have their columns changed. - Observable.merge(...this._rowDefinitions.map(rowDef => rowDef.columnsChange)) - .takeUntil(this._onDestroy) - .subscribe(() => { - // Reset the data to an empty array so that renderRowChanges will re-render all new rows. - this._rowPlaceholder.viewContainer.clear(); - this._dataDiffer.diff([]); - this._renderRowChanges(); - }); + const columnChangeEvents = this._rowDefinitions.map(rowDef => rowDef.columnsChange); + + takeUntil.call(merge(...columnChangeEvents), this._onDestroy).subscribe(() => { + // Reset the data to an empty array so that renderRowChanges will re-render all new rows. + this._rowPlaceholder.viewContainer.clear(); + this._dataDiffer.diff([]); + this._renderRowChanges(); + }); // Re-render the header row if the columns change - this._headerDefinition.columnsChange - .takeUntil(this._onDestroy) - .subscribe(() => { - this._headerRowPlaceholder.viewContainer.clear(); - this._renderHeaderRow(); - }); + takeUntil.call(this._headerDefinition.columnsChange, this._onDestroy).subscribe(() => { + this._headerRowPlaceholder.viewContainer.clear(); + this._renderHeaderRow(); + }); } ngAfterViewInit() { @@ -231,12 +227,11 @@ export class CdkTable implements CollectionViewer { /** Set up a subscription for the data provided by the data source. */ private _observeRenderChanges() { - this._renderChangeSubscription = this.dataSource.connect(this) - .takeUntil(this._onDestroy) - .subscribe(data => { - this._data = data; - this._renderRowChanges(); - }); + this._renderChangeSubscription = takeUntil.call(this.dataSource.connect(this), this._onDestroy) + .subscribe(data => { + this._data = data; + this._renderRowChanges(); + }); } /** diff --git a/src/lib/core/observe-content/observe-content.ts b/src/lib/core/observe-content/observe-content.ts index f25580672ebe..9188a562e70f 100644 --- a/src/lib/core/observe-content/observe-content.ts +++ b/src/lib/core/observe-content/observe-content.ts @@ -18,7 +18,7 @@ import { Injectable, } from '@angular/core'; import {Subject} from 'rxjs/Subject'; -import 'rxjs/add/operator/debounceTime'; +import {RxChain, debounceTime} from '../rxjs/index'; /** * Factory that creates a new MutationObserver and allows us to stub it out in unit tests. @@ -56,9 +56,9 @@ export class ObserveContent implements AfterContentInit, OnDestroy { ngAfterContentInit() { if (this.debounce > 0) { - this._debouncer - .debounceTime(this.debounce) - .subscribe(mutations => this.event.emit(mutations)); + RxChain.from(this._debouncer) + .call(debounceTime, this.debounce) + .subscribe((mutations: MutationRecord[]) => this.event.emit(mutations)); } else { this._debouncer.subscribe(mutations => this.event.emit(mutations)); } diff --git a/src/lib/core/overlay/scroll/scroll-dispatcher.ts b/src/lib/core/overlay/scroll/scroll-dispatcher.ts index fb3d5eefca4c..a1db9a9e7abd 100644 --- a/src/lib/core/overlay/scroll/scroll-dispatcher.ts +++ b/src/lib/core/overlay/scroll/scroll-dispatcher.ts @@ -10,11 +10,10 @@ import {ElementRef, Injectable, NgZone, Optional, SkipSelf} from '@angular/core' import {Platform} from '../../platform/index'; import {Scrollable} from './scrollable'; import {Subject} from 'rxjs/Subject'; -import {Observable} from 'rxjs/Observable'; import {Subscription} from 'rxjs/Subscription'; -import 'rxjs/add/observable/fromEvent'; -import 'rxjs/add/observable/merge'; -import 'rxjs/add/operator/auditTime'; +import {fromEvent} from 'rxjs/observable/fromEvent'; +import {merge} from 'rxjs/observable/merge'; +import {auditTime} from '../../rxjs/index'; /** Time in ms to throttle the scrolling events by default. */ @@ -81,16 +80,16 @@ export class ScrollDispatcher { // In the case of a 0ms delay, use an observable without auditTime // since it does add a perceptible delay in processing overhead. let observable = auditTimeInMs > 0 ? - this._scrolled.asObservable().auditTime(auditTimeInMs) : + auditTime.call(this._scrolled.asObservable(), auditTimeInMs) : this._scrolled.asObservable(); this._scrolledCount++; if (!this._globalSubscription) { this._globalSubscription = this._ngZone.runOutsideAngular(() => { - return Observable.merge( - Observable.fromEvent(window.document, 'scroll'), - Observable.fromEvent(window, 'resize') + return merge( + fromEvent(window.document, 'scroll'), + fromEvent(window, 'resize') ).subscribe(() => this._notify()); }); } diff --git a/src/lib/core/overlay/scroll/scrollable.ts b/src/lib/core/overlay/scroll/scrollable.ts index cb9aa9bc5470..17e185e75a67 100644 --- a/src/lib/core/overlay/scroll/scrollable.ts +++ b/src/lib/core/overlay/scroll/scrollable.ts @@ -10,7 +10,6 @@ import {Directive, ElementRef, OnInit, OnDestroy, NgZone, Renderer2} from '@angu import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {ScrollDispatcher} from './scroll-dispatcher'; -import 'rxjs/add/observable/fromEvent'; /** diff --git a/src/lib/core/rxjs/index.ts b/src/lib/core/rxjs/index.ts new file mode 100644 index 000000000000..378572f1941f --- /dev/null +++ b/src/lib/core/rxjs/index.ts @@ -0,0 +1,2 @@ +export * from './rx-chain'; +export * from './rx-operators'; diff --git a/src/lib/core/rxjs/rx-chain.spec.ts b/src/lib/core/rxjs/rx-chain.spec.ts new file mode 100644 index 000000000000..6be978f0db2a --- /dev/null +++ b/src/lib/core/rxjs/rx-chain.spec.ts @@ -0,0 +1,41 @@ +import {Observable} from 'rxjs/Observable'; +import {of as observableOf} from 'rxjs/observable/of'; +import {RxChain, map, filter, first} from './index'; + +describe('RxChain', () => { + it('should call all of the operators in the chain', () => { + let operators = { map, filter, first }; + + spyOn(operators, 'map'); + spyOn(operators, 'filter'); + spyOn(operators, 'first'); + + RxChain.from(observableOf(1, 2, 3)) + .call(operators.map, i => i * 2) + .call(operators.filter, i => i % 2 === 0) + .call(operators.first); + + expect(operators.map).toHaveBeenCalled(); + expect(operators.filter).toHaveBeenCalled(); + expect(operators.first).toHaveBeenCalled(); + }); + + it('should be able to subscribe', () => { + const spy = jasmine.createSpy('subscription spy'); + + RxChain.from(observableOf(1, 2)) + .call(map, i => i * 2) + .call(first) + .subscribe(spy); + + expect(spy).toHaveBeenCalledWith(2); + }); + + it('should be able to return the result observable', () => { + const chain = RxChain.from(observableOf(1, 2)) + .call(map, i => i * 2) + .call(first); + + expect(chain.result() instanceof Observable).toBe(true); + }); +}); diff --git a/src/lib/core/rxjs/rx-chain.ts b/src/lib/core/rxjs/rx-chain.ts new file mode 100644 index 000000000000..9d36abdcf5dc --- /dev/null +++ b/src/lib/core/rxjs/rx-chain.ts @@ -0,0 +1,52 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Observable} from 'rxjs/Observable'; +import {Subscription} from 'rxjs/Subscription'; +import {StrictRxChain} from './rx-operators'; + +/** + * Utility class used to chain RxJS operators. + * @docs-private + */ +export class RxChain { + constructor(private _context: Observable) { } + + /** + * Starts a new chain and specifies the initial `this` value. + * @param context Initial `this` value for the chain. + */ + static from(context: Observable): StrictRxChain { + return new RxChain(context); + } + + /** + * Invokes an RxJS operator as a part of the chain. + * @param operator Operator to be invoked. + * @param args Arguments to be passed to the operator. + */ + call(operator: Function, ...args: any[]): RxChain { + this._context = operator.call(this._context, ...args); + return this; + } + + /** + * Subscribes to the result of the chain. + * @param fn Callback to be invoked when the result emits a value. + */ + subscribe(fn: (t: T) => void): Subscription { + return this._context.subscribe(fn); + } + + /** + * Returns the result of the chain. + */ + result(): Observable { + return this._context; + } +} diff --git a/src/lib/core/rxjs/rx-operators.ts b/src/lib/core/rxjs/rx-operators.ts new file mode 100644 index 000000000000..0af612da4f02 --- /dev/null +++ b/src/lib/core/rxjs/rx-operators.ts @@ -0,0 +1,144 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ + +import {Observable, ObservableInput} from 'rxjs/Observable'; +import {PartialObserver} from 'rxjs/Observer'; +import {Subscription} from 'rxjs/Subscription'; +import {Scheduler, IScheduler} from 'rxjs/Scheduler'; +import {_finally as _finallyOperator} from 'rxjs/operator/finally'; +import {_catch as _catchOperator} from 'rxjs/operator/catch'; +import {_do as _doOperator} from 'rxjs/operator/do'; +import {map as mapOperator} from 'rxjs/operator/map'; +import {filter as filterOperator} from 'rxjs/operator/filter'; +import {share as shareOperator} from 'rxjs/operator/share'; +import {first as firstOperator} from 'rxjs/operator/first'; +import {switchMap as switchMapOperator} from 'rxjs/operator/switchMap'; +import {startWith as startWithOperator} from 'rxjs/operator/startWith'; +import {debounceTime as debounceTimeOperator} from 'rxjs/operator/debounceTime'; +import {auditTime as auditTimeOperator} from 'rxjs/operator/auditTime'; +import {takeUntil as takeUntilOperator} from 'rxjs/operator/takeUntil'; + +/** + * Represents a strongly-typed chain of RxJS operators. + * @docs-private + */ +export interface StrictRxChain { + call(operator: mapOperatorType, + project: (value: T, index: number) => R, thisArg?: any): StrictRxChain; + + call(operator: switchMapOperatorType, + project: (value: T, index: number) => ObservableInput): StrictRxChain; + + call(operator: catchOperatorType, + selector: (err: any, caught: Observable) => ObservableInput): StrictRxChain; + + call(operator: filterOperatorType, + predicate: (value: T, index: number) => boolean, thisArg?: any): StrictRxChain; + + call(operator: shareOperatorType): StrictRxChain; + + call(operator: finallyOperatorType, action: () => void): StrictRxChain; + + call(operator: doOperatorType, next: (x: T) => void, error?: + (e: any) => void, complete?: () => void): StrictRxChain; + + call(operator: doOperatorType, observer: PartialObserver): StrictRxChain; + + call(operator: firstOperatorType, thisArg?: any, defaultValue?: any): StrictRxChain; + + call(operator: firstOperatorType, predicate: (value: T) => boolean): StrictRxChain; + + call(operator: startWithOperatorType, ...args: any[]): StrictRxChain; + + call(operator: debounceTimeOperatorType, dueTime: number, + scheduler?: IScheduler): StrictRxChain; + + call(operator: auditTimeOperatorType, duration: number, + scheduler?: IScheduler): StrictRxChain; + + call(operator: takeUntilOperatorType, notifier: Observable): StrictRxChain; + + subscribe(fn: (t: T) => void): Subscription; + + result(): Observable; +} + +// @docs-private +export class FinallyBrand { private _; } +// @docs-private +export class CatchBrand { private _; } +// @docs-private +export class DoBrand { private _; } +// @docs-private +export class MapBrand { private _; } +// @docs-private +export class FilterBrand { private _; } +// @docs-private +export class ShareBrand { private _; } +// @docs-private +export class FirstBrand { private _; } +// @docs-private +export class SwitchMapBrand { private _; } +// @docs-private +export class StartWithBrand { private _; } +// @docs-private +export class DebounceTimeBrand { private _; } +// @docs-private +export class AuditTimeBrand { private _; } +// @docs-private +export class TakeUntilBrand { private _; } + +// @docs-private +export type finallyOperatorType = typeof _finallyOperator & FinallyBrand; +// @docs-private +export type catchOperatorType = typeof _catchOperator & CatchBrand; +// @docs-private +export type doOperatorType = typeof _doOperator & DoBrand; +// @docs-private +export type mapOperatorType = typeof mapOperator & MapBrand; +// @docs-private +export type filterOperatorType = typeof filterOperator & FilterBrand; +// @docs-private +export type shareOperatorType = typeof shareOperator & ShareBrand; +// @docs-private +export type firstOperatorType = typeof firstOperator & FirstBrand; +// @docs-private +export type switchMapOperatorType = typeof switchMapOperator & SwitchMapBrand; +// @docs-private +export type startWithOperatorType = typeof startWithOperator & StartWithBrand; +// @docs-private +export type debounceTimeOperatorType = typeof debounceTimeOperator & DebounceTimeBrand; +// @docs-private +export type auditTimeOperatorType = typeof auditTimeOperator & AuditTimeBrand; +// @docs-private +export type takeUntilOperatorType = typeof takeUntilOperator & TakeUntilBrand; + +// @docs-private +export const finallyOperator = _finallyOperator as typeof _finallyOperator & FinallyBrand; +// @docs-private +export const catchOperator = _catchOperator as typeof _catchOperator & CatchBrand; +// @docs-private +export const doOperator = _doOperator as typeof _doOperator & DoBrand; +// @docs-private +export const map = mapOperator as typeof mapOperator & MapBrand; +// @docs-private +export const filter = filterOperator as typeof filterOperator & FilterBrand; +// @docs-private +export const share = shareOperator as typeof shareOperator & ShareBrand; +// @docs-private +export const first = firstOperator as typeof firstOperator & FirstBrand; +// @docs-private +export const switchMap = switchMapOperator as typeof switchMapOperator & SwitchMapBrand; +// @docs-private +export const startWith = startWithOperator as typeof startWithOperator & StartWithBrand; +// @docs-private +export const debounceTime = debounceTimeOperator as typeof debounceTimeOperator & DebounceTimeBrand; +// @docs-private +export const auditTime = auditTimeOperator as typeof auditTimeOperator & AuditTimeBrand; +// @docs-private +export const takeUntil = takeUntilOperator as typeof takeUntilOperator & TakeUntilBrand; diff --git a/src/lib/core/style/focus-origin-monitor.ts b/src/lib/core/style/focus-origin-monitor.ts index e4a069d9250e..5a952a74cab1 100644 --- a/src/lib/core/style/focus-origin-monitor.ts +++ b/src/lib/core/style/focus-origin-monitor.ts @@ -21,8 +21,7 @@ import { import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {Platform} from '../platform/platform'; - -import 'rxjs/add/observable/of'; +import {of as observableOf} from 'rxjs/observable/of'; // This is the value used by AngularJS Material. Through trial and error (on iPhone 6S) they found @@ -80,7 +79,7 @@ export class FocusOriginMonitor { checkChildren: boolean): Observable { // Do nothing if we're not on the browser platform. if (!this._platform.isBrowser) { - return Observable.of(null); + return observableOf(null); } // Check if we're already monitoring this element. if (this._elementInfo.has(element)) { diff --git a/src/lib/datepicker/calendar.ts b/src/lib/datepicker/calendar.ts index 1df35403115c..7bfccbbb1042 100644 --- a/src/lib/datepicker/calendar.ts +++ b/src/lib/datepicker/calendar.ts @@ -35,6 +35,7 @@ import {MdDatepickerIntl} from './datepicker-intl'; import {createMissingDateImplError} from './datepicker-errors'; import {MD_DATE_FORMATS, MdDateFormats} from '../core/datetime/date-formats'; import {MATERIAL_COMPATIBILITY_MODE} from '../core'; +import {first} from '../core/rxjs/index'; /** @@ -196,9 +197,8 @@ export class MdCalendar implements AfterContentInit { /** Focuses the active cell after the microtask queue is empty. */ _focusActiveCell() { - this._ngZone.runOutsideAngular(() => this._ngZone.onStable.first().subscribe(() => { - let activeEl = this._elementRef.nativeElement.querySelector('.mat-calendar-body-active'); - activeEl.focus(); + this._ngZone.runOutsideAngular(() => first.call(this._ngZone.onStable).subscribe(() => { + this._elementRef.nativeElement.querySelector('.mat-calendar-body-active').focus(); })); } diff --git a/src/lib/datepicker/datepicker.ts b/src/lib/datepicker/datepicker.ts index d218d55fb624..685c689dee9f 100644 --- a/src/lib/datepicker/datepicker.ts +++ b/src/lib/datepicker/datepicker.ts @@ -38,7 +38,7 @@ import {DateAdapter} from '../core/datetime/index'; import {createMissingDateImplError} from './datepicker-errors'; import {ESCAPE} from '../core/keyboard/keycodes'; import {MdCalendar} from './calendar'; -import 'rxjs/add/operator/first'; +import {first} from '../core/rxjs/index'; /** Used to generate a unique ID for each datepicker instance. */ @@ -269,7 +269,7 @@ export class MdDatepicker implements OnDestroy { componentRef.instance.datepicker = this; // Update the position once the calendar has rendered. - this._ngZone.onStable.first().subscribe(() => this._popupRef.updatePosition()); + first.call(this._ngZone.onStable).subscribe(() => this._popupRef.updatePosition()); } this._popupRef.backdropClick().subscribe(() => this.close()); diff --git a/src/lib/dialog/dialog-ref.ts b/src/lib/dialog/dialog-ref.ts index cbe84e1815b4..dd6c07943bda 100644 --- a/src/lib/dialog/dialog-ref.ts +++ b/src/lib/dialog/dialog-ref.ts @@ -12,7 +12,7 @@ import {DialogPosition} from './dialog-config'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {MdDialogContainer} from './dialog-container'; -import 'rxjs/add/operator/filter'; +import {filter} from '../core/rxjs/index'; // TODO(jelbourn): resizing @@ -36,8 +36,8 @@ export class MdDialogRef { private _result: any; constructor(private _overlayRef: OverlayRef, private _containerInstance: MdDialogContainer) { - _containerInstance._onAnimationStateChange - .filter((event: AnimationEvent) => event.toState === 'exit') + filter.call(_containerInstance._onAnimationStateChange, + (event: AnimationEvent) => event.toState === 'exit') .subscribe(() => this._overlayRef.dispose(), undefined, () => { this._afterClosed.next(this._result); this._afterClosed.complete(); diff --git a/src/lib/icon/icon-registry.ts b/src/lib/icon/icon-registry.ts index 54e196e21fe9..9c590e874936 100644 --- a/src/lib/icon/icon-registry.ts +++ b/src/lib/icon/icon-registry.ts @@ -10,16 +10,10 @@ import {Injectable, SecurityContext, Optional, SkipSelf} from '@angular/core'; import {SafeResourceUrl, DomSanitizer} from '@angular/platform-browser'; import {Http} from '@angular/http'; import {Observable} from 'rxjs/Observable'; -import 'rxjs/add/observable/forkJoin'; -import 'rxjs/add/observable/of'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/filter'; -import 'rxjs/add/operator/do'; -import 'rxjs/add/operator/share'; -import 'rxjs/add/operator/finally'; -import 'rxjs/add/operator/catch'; -import 'rxjs/add/observable/throw'; - +import {_throw as observableThrow} from 'rxjs/observable/throw'; +import {of as observableOf} from 'rxjs/observable/of'; +import {forkJoin} from 'rxjs/observable/forkJoin'; +import {RxChain, map, doOperator, catchOperator, finallyOperator, share} from '../core/rxjs/index'; /** * Returns an exception to be thrown in the case when attempting to @@ -203,12 +197,13 @@ export class MdIconRegistry { let cachedIcon = this._cachedIconsByUrl.get(url); if (cachedIcon) { - return Observable.of(cloneSvg(cachedIcon)); + return observableOf(cloneSvg(cachedIcon)); } - return this._loadSvgIconFromConfig(new SvgIconConfig(url)) - .do(svg => this._cachedIconsByUrl.set(url!, svg)) - .map(svg => cloneSvg(svg)); + return RxChain.from(this._loadSvgIconFromConfig(new SvgIconConfig(url))) + .call(doOperator, svg => this._cachedIconsByUrl.set(url!, svg)) + .call(map, svg => cloneSvg(svg)) + .result(); } /** @@ -227,12 +222,15 @@ export class MdIconRegistry { if (config) { return this._getSvgFromConfig(config); } + // See if we have any icon sets registered for the namespace. const iconSetConfigs = this._iconSetConfigs.get(namespace); + if (iconSetConfigs) { return this._getSvgFromIconSetConfigs(name, iconSetConfigs); } - return Observable.throw(getMdIconNameNotFoundError(key)); + + return observableThrow(getMdIconNameNotFoundError(key)); } /** @@ -241,12 +239,13 @@ export class MdIconRegistry { private _getSvgFromConfig(config: SvgIconConfig): Observable { if (config.svgElement) { // We already have the SVG element for this icon, return a copy. - return Observable.of(cloneSvg(config.svgElement)); + return observableOf(cloneSvg(config.svgElement)); } else { // Fetch the icon from the config's URL, cache it, and return a copy. - return this._loadSvgIconFromConfig(config) - .do(svg => config.svgElement = svg) - .map(svg => cloneSvg(svg)); + return RxChain.from(this._loadSvgIconFromConfig(config)) + .call(doOperator, svg => config.svgElement = svg) + .call(map, svg => cloneSvg(svg)) + .result(); } } @@ -263,43 +262,48 @@ export class MdIconRegistry { // For all the icon set SVG elements we've fetched, see if any contain an icon with the // requested name. const namedIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs); + if (namedIcon) { // We could cache namedIcon in _svgIconConfigs, but since we have to make a copy every // time anyway, there's probably not much advantage compared to just always extracting // it from the icon set. - return Observable.of(namedIcon); + return observableOf(namedIcon); } + // Not found in any cached icon sets. If there are icon sets with URLs that we haven't // fetched, fetch them now and look for iconName in the results. const iconSetFetchRequests: Observable[] = iconSetConfigs - .filter(iconSetConfig => !iconSetConfig.svgElement) - .map(iconSetConfig => - this._loadSvgIconSetFromConfig(iconSetConfig) - .catch((err: any): Observable => { - let url = - this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, iconSetConfig.url); - - // Swallow errors fetching individual URLs so the combined Observable won't - // necessarily fail. - console.log(`Loading icon set URL: ${url} failed: ${err}`); - return Observable.of(null); - }) - .do(svg => { - // Cache SVG element. - if (svg) { - iconSetConfig.svgElement = svg; - } - })); + .filter(iconSetConfig => !iconSetConfig.svgElement) + .map(iconSetConfig => { + return RxChain.from(this._loadSvgIconSetFromConfig(iconSetConfig)) + .call(catchOperator, (err: any): Observable => { + let url = this._sanitizer.sanitize(SecurityContext.RESOURCE_URL, iconSetConfig.url); + + // Swallow errors fetching individual URLs so the combined Observable won't + // necessarily fail. + console.log(`Loading icon set URL: ${url} failed: ${err}`); + return observableOf(null); + }) + .call(doOperator, svg => { + // Cache the SVG element. + if (svg) { + iconSetConfig.svgElement = svg; + } + }) + .result(); + }); + // Fetch all the icon set URLs. When the requests complete, every IconSet should have a // cached SVG element (unless the request failed), and we can check again for the icon. - return Observable.forkJoin(iconSetFetchRequests) - .map(() => { - const foundIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs); - if (!foundIcon) { - throw getMdIconNameNotFoundError(name); - } - return foundIcon; - }); + return map.call(forkJoin.call(Observable, iconSetFetchRequests), () => { + const foundIcon = this._extractIconWithNameFromAnySet(name, iconSetConfigs); + + if (!foundIcon) { + throw getMdIconNameNotFoundError(name); + } + + return foundIcon; + }); } /** @@ -327,8 +331,8 @@ export class MdIconRegistry { * from it. */ private _loadSvgIconFromConfig(config: SvgIconConfig): Observable { - return this._fetchUrl(config.url) - .map(svgText => this._createSvgElementForSingleIcon(svgText)); + return map.call(this._fetchUrl(config.url), + svgText => this._createSvgElementForSingleIcon(svgText)); } /** @@ -337,8 +341,8 @@ export class MdIconRegistry { */ private _loadSvgIconSetFromConfig(config: SvgIconConfig): Observable { // TODO: Document that icons should only be loaded from trusted sources. - return this._fetchUrl(config.url) - .map(svgText => this._svgElementFromString(svgText)); + return map.call(this._fetchUrl(config.url), + svgText => this._svgElementFromString(svgText)); } /** @@ -458,12 +462,12 @@ export class MdIconRegistry { // TODO(jelbourn): for some reason, the `finally` operator "loses" the generic type on the // Observable. Figure out why and fix it. - const req = > this._http.get(url) - .map(response => response.text()) - .finally(() => { - this._inProgressUrlFetches.delete(url); - }) - .share(); + const req = RxChain.from(this._http.get(url)) + .call(map, response => response.text()) + .call(finallyOperator, () => this._inProgressUrlFetches.delete(url)) + .call(share) + .result(); + this._inProgressUrlFetches.set(url, req); return req; } diff --git a/src/lib/icon/icon.ts b/src/lib/icon/icon.ts index 17e9c82621e9..7c52f8839a14 100644 --- a/src/lib/icon/icon.ts +++ b/src/lib/icon/icon.ts @@ -20,6 +20,7 @@ import { } from '@angular/core'; import {MdIconRegistry} from './icon-registry'; import {CanColor, mixinColor} from '../core/common-behaviors/color'; +import {first} from '../core/rxjs/index'; // Boilerplate for applying mixins to MdIcon. @@ -138,7 +139,7 @@ export class MdIcon extends _MdIconMixinBase implements OnChanges, OnInit, CanCo if (changedInputs.indexOf('svgIcon') != -1 || changedInputs.indexOf('svgSrc') != -1) { if (this.svgIcon) { const [namespace, iconName] = this._splitIconName(this.svgIcon); - this._mdIconRegistry.getNamedSvgIcon(iconName, namespace).first().subscribe( + first.call(this._mdIconRegistry.getNamedSvgIcon(iconName, namespace)).subscribe( svg => this._setSvgElement(svg), (err: Error) => console.log(`Error retrieving icon: ${err.message}`)); } diff --git a/src/lib/select/select.ts b/src/lib/select/select.ts index e00d54ef1827..1c8ed327f7b7 100644 --- a/src/lib/select/select.ts +++ b/src/lib/select/select.ts @@ -39,9 +39,8 @@ import {ConnectedOverlayDirective} from '../core/overlay/overlay-directives'; import {ViewportRuler} from '../core/overlay/position/viewport-ruler'; import {SelectionModel} from '../core/selection/selection'; import {getMdSelectDynamicMultipleError, getMdSelectNonArrayValueError} from './select-errors'; -import 'rxjs/add/observable/merge'; -import 'rxjs/add/operator/startWith'; -import 'rxjs/add/operator/filter'; +import {startWith, filter} from '../core/rxjs/index'; +import {merge} from 'rxjs/observable/merge'; import {CanColor, mixinColor} from '../core/common-behaviors/color'; import {CanDisable, mixinDisabled} from '../core/common-behaviors/disabled'; import { @@ -305,7 +304,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Combined stream of all of the child options' change events. */ get optionSelectionChanges(): Observable { - return Observable.merge(...this.options.map(option => option.onSelectionChange)); + return merge(...this.options.map(option => option.onSelectionChange)); } /** Event emitted when the select has been opened. */ @@ -333,7 +332,6 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On } this._tabIndex = parseInt(tabIndex) || 0; - this._placeholderOptions = placeholderOptions ? placeholderOptions : {}; this.floatPlaceholder = this._placeholderOptions.float || 'auto'; } @@ -345,7 +343,7 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On ngAfterContentInit() { this._initKeyManager(); - this._changeSubscription = this.options.changes.startWith(null).subscribe(() => { + this._changeSubscription = startWith.call(this.options.changes, null).subscribe(() => { this._resetOptions(); if (this._control) { @@ -640,9 +638,8 @@ export class MdSelect extends _MdSelectMixinBase implements AfterContentInit, On /** Listens to user-generated selection events on each option. */ private _listenToOptions(): void { - this._optionSubscription = this.optionSelectionChanges - .filter(event => event.isUserInput) - .subscribe(event => { + this._optionSubscription = filter.call(this.optionSelectionChanges, + event => event.isUserInput).subscribe(event => { this._onSelect(event.source); this._setValueWidth(); diff --git a/src/lib/sidenav/sidenav.ts b/src/lib/sidenav/sidenav.ts index 79dcb17107a7..ca2cd5382e31 100644 --- a/src/lib/sidenav/sidenav.ts +++ b/src/lib/sidenav/sidenav.ts @@ -20,12 +20,13 @@ import { Renderer2, ViewEncapsulation, NgZone, - OnDestroy, Inject, + OnDestroy, + Inject, } from '@angular/core'; import {Directionality, coerceBooleanProperty} from '../core'; import {FocusTrapFactory, FocusTrap} from '../core/a11y/focus-trap'; import {ESCAPE} from '../core/keyboard/keycodes'; -import 'rxjs/add/operator/first'; +import {first} from '../core/rxjs/index'; import {DOCUMENT} from '@angular/platform-browser'; @@ -384,7 +385,7 @@ export class MdSidenavContainer implements AfterContentInit { this._validateDrawers(); // Give the view a chance to render the initial state, then enable transitions. - this._ngZone.onMicrotaskEmpty.first().subscribe(() => this._enableTransitions = true); + first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => this._enableTransitions = true); } /** Calls `open` of both start and end sidenavs */ @@ -423,7 +424,7 @@ export class MdSidenavContainer implements AfterContentInit { // NOTE: We need to wait for the microtask queue to be empty before validating, // since both drawers may be swapping sides at the same time. sidenav.onAlignChanged.subscribe(() => - this._ngZone.onMicrotaskEmpty.first().subscribe(() => this._validateDrawers())); + first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => this._validateDrawers())); } /** Toggles the 'mat-sidenav-opened' class on the main 'md-sidenav-container' element. */ diff --git a/src/lib/snack-bar/snack-bar-container.ts b/src/lib/snack-bar/snack-bar-container.ts index 9677b96a0ed3..487b26385c59 100644 --- a/src/lib/snack-bar/snack-bar-container.ts +++ b/src/lib/snack-bar/snack-bar-container.ts @@ -32,6 +32,7 @@ import { import {MdSnackBarConfig} from './snack-bar-config'; import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; +import {first} from '../core/rxjs/index'; @@ -167,7 +168,7 @@ export class MdSnackBarContainer extends BasePortalHost implements OnDestroy { // because it can cause a memory leak. const onExit = this.onExit; - this._ngZone.onMicrotaskEmpty.first().subscribe(() => { + first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => { onExit.next(); onExit.complete(); }); diff --git a/src/lib/tabs/tab-body.ts b/src/lib/tabs/tab-body.ts index 9fc18ff700e9..ec3a42bb9feb 100644 --- a/src/lib/tabs/tab-body.ts +++ b/src/lib/tabs/tab-body.ts @@ -27,7 +27,6 @@ import { AnimationEvent, } from '@angular/animations'; import {TemplatePortal, PortalHostDirective, Directionality, Direction} from '../core'; -import 'rxjs/add/operator/map'; /** * These position states are used internally as animation states for the tab body. Setting the diff --git a/src/lib/tabs/tab-group.ts b/src/lib/tabs/tab-group.ts index b52503e96917..1e1b6d07ceef 100644 --- a/src/lib/tabs/tab-group.ts +++ b/src/lib/tabs/tab-group.ts @@ -20,7 +20,7 @@ import { import {coerceBooleanProperty} from '../core'; import {Observable} from 'rxjs/Observable'; import {MdTab} from './tab'; -import 'rxjs/add/operator/map'; +import {map} from '../core/rxjs/index'; /** Used to generate unique ID's for each tab component */ @@ -96,7 +96,7 @@ export class MdTabGroup { /** Output to enable support for two-way binding on `[(selectedIndex)]` */ @Output() get selectedIndexChange(): Observable { - return this.selectChange.map(event => event.index); + return map.call(this.selectChange, event => event.index); } /** Event emitted when focus has changed within a tab group. */ diff --git a/src/lib/tabs/tab-header.ts b/src/lib/tabs/tab-header.ts index a78cbe980e32..e71a7e4a1450 100644 --- a/src/lib/tabs/tab-header.ts +++ b/src/lib/tabs/tab-header.ts @@ -33,13 +33,11 @@ import { import {MdTabLabelWrapper} from './tab-label-wrapper'; import {MdInkBar} from './ink-bar'; import {Subscription} from 'rxjs/Subscription'; -import {Observable} from 'rxjs/Observable'; import {applyCssTransform} from '../core/style/apply-transform'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/auditTime'; -import 'rxjs/add/observable/of'; -import 'rxjs/add/observable/merge'; -import 'rxjs/add/operator/startWith'; +import {auditTime, startWith} from '../core/rxjs/index'; +import {of as observableOf} from 'rxjs/observable/of'; +import {merge} from 'rxjs/observable/merge'; +import {fromEvent} from 'rxjs/observable/fromEvent'; /** @@ -183,12 +181,12 @@ export class MdTabHeader implements AfterContentChecked, AfterContentInit, OnDes */ ngAfterContentInit() { this._realignInkBar = this._ngZone.runOutsideAngular(() => { - let dirChange = this._dir ? this._dir.change : Observable.of(null); + let dirChange = this._dir ? this._dir.change : observableOf(null); let resize = typeof window !== 'undefined' ? - Observable.fromEvent(window, 'resize').auditTime(10) : - Observable.of(null); + auditTime.call(fromEvent(window, 'resize'), 10) : + observableOf(null); - return Observable.merge(dirChange, resize).startWith(null).subscribe(() => { + return startWith.call(merge(dirChange, resize), null).subscribe(() => { this._updatePagination(); this._alignInkBarToSelectedTab(); }); diff --git a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts index 34689baceacc..3e762ecd82c7 100644 --- a/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts +++ b/src/lib/tabs/tab-nav-bar/tab-nav-bar.ts @@ -23,12 +23,11 @@ import {MdInkBar} from '../ink-bar'; import {MdRipple} from '../../core/ripple/index'; import {ViewportRuler} from '../../core/overlay/position/viewport-ruler'; import {Directionality, MD_RIPPLE_GLOBAL_OPTIONS, Platform, RippleGlobalOptions} from '../../core'; -import {Observable} from 'rxjs/Observable'; -import 'rxjs/add/operator/auditTime'; -import 'rxjs/add/operator/takeUntil'; -import 'rxjs/add/observable/of'; -import 'rxjs/add/observable/merge'; import {Subject} from 'rxjs/Subject'; +import {takeUntil, auditTime} from '../../core/rxjs/index'; +import {of as observableOf} from 'rxjs/observable/of'; +import {merge} from 'rxjs/observable/merge'; +import {fromEvent} from 'rxjs/observable/fromEvent'; /** * Navigation component matching the styles of the tab group header. @@ -61,13 +60,12 @@ export class MdTabNav implements AfterContentInit, OnDestroy { ngAfterContentInit(): void { this._ngZone.runOutsideAngular(() => { - let dirChange = this._dir ? this._dir.change : Observable.of(null); + let dirChange = this._dir ? this._dir.change : observableOf(null); let resize = typeof window !== 'undefined' ? - Observable.fromEvent(window, 'resize').auditTime(10) : - Observable.of(null); + auditTime.call(fromEvent(window, 'resize'), 10) : + observableOf(null); - return Observable.merge(dirChange, resize) - .takeUntil(this._onDestroy) + return takeUntil.call(merge(dirChange, resize), this._onDestroy) .subscribe(() => this._alignInkBar()); }); } diff --git a/src/lib/tooltip/tooltip.ts b/src/lib/tooltip/tooltip.ts index 281a6260bc7e..261b60a381b4 100644 --- a/src/lib/tooltip/tooltip.ts +++ b/src/lib/tooltip/tooltip.ts @@ -39,7 +39,7 @@ import {Observable} from 'rxjs/Observable'; import {Subject} from 'rxjs/Subject'; import {Directionality} from '../core/bidi/index'; import {Platform} from '../core/platform/index'; -import 'rxjs/add/operator/first'; +import {first} from '../core/rxjs/index'; import {ScrollDispatcher} from '../core/overlay/scroll/scroll-dispatcher'; import {coerceBooleanProperty} from '@angular/cdk'; @@ -339,7 +339,7 @@ export class MdTooltip implements OnDestroy { this._tooltipInstance.message = message; this._tooltipInstance._markForCheck(); - this._ngZone.onMicrotaskEmpty.first().subscribe(() => { + first.call(this._ngZone.onMicrotaskEmpty).subscribe(() => { if (this._tooltipInstance) { this._overlayRef!.updatePosition(); } diff --git a/tools/package-tools/rollup-helpers.ts b/tools/package-tools/rollup-helpers.ts index fabc11f494fe..2aa8d794421b 100644 --- a/tools/package-tools/rollup-helpers.ts +++ b/tools/package-tools/rollup-helpers.ts @@ -29,26 +29,26 @@ const ROLLUP_GLOBALS = { 'rxjs/Observable': 'Rx', 'rxjs/Subject': 'Rx', 'rxjs/Subscription': 'Rx', - 'rxjs/add/observable/combineLatest': 'Rx.Observable', - 'rxjs/add/observable/forkJoin': 'Rx.Observable', - 'rxjs/add/observable/fromEvent': 'Rx.Observable', - 'rxjs/add/observable/merge': 'Rx.Observable', - 'rxjs/add/observable/of': 'Rx.Observable', - 'rxjs/add/observable/throw': 'Rx.Observable', - 'rxjs/add/operator/auditTime': 'Rx.Observable.prototype', - 'rxjs/add/operator/catch': 'Rx.Observable.prototype', - 'rxjs/add/operator/debounceTime': 'Rx.Observable.prototype', - 'rxjs/add/operator/do': 'Rx.Observable.prototype', - 'rxjs/add/operator/filter': 'Rx.Observable.prototype', - 'rxjs/add/operator/finally': 'Rx.Observable.prototype', - 'rxjs/add/operator/first': 'Rx.Observable.prototype', - 'rxjs/add/operator/let': 'Rx.Observable.prototype', - 'rxjs/add/operator/map': 'Rx.Observable.prototype', - 'rxjs/add/operator/share': 'Rx.Observable.prototype', - 'rxjs/add/operator/startWith': 'Rx.Observable.prototype', - 'rxjs/add/operator/switchMap': 'Rx.Observable.prototype', - 'rxjs/add/operator/takeUntil': 'Rx.Observable.prototype', - 'rxjs/add/operator/toPromise': 'Rx.Observable.prototype', + 'rxjs/observable/combineLatest': 'Rx.Observable', + 'rxjs/observable/forkJoin': 'Rx.Observable', + 'rxjs/observable/fromEvent': 'Rx.Observable', + 'rxjs/observable/merge': 'Rx.Observable', + 'rxjs/observable/of': 'Rx.Observable', + 'rxjs/observable/throw': 'Rx.Observable', + 'rxjs/operator/auditTime': 'Rx.Observable.prototype', + 'rxjs/operator/catch': 'Rx.Observable.prototype', + 'rxjs/operator/debounceTime': 'Rx.Observable.prototype', + 'rxjs/operator/do': 'Rx.Observable.prototype', + 'rxjs/operator/filter': 'Rx.Observable.prototype', + 'rxjs/operator/finally': 'Rx.Observable.prototype', + 'rxjs/operator/first': 'Rx.Observable.prototype', + 'rxjs/operator/let': 'Rx.Observable.prototype', + 'rxjs/operator/map': 'Rx.Observable.prototype', + 'rxjs/operator/share': 'Rx.Observable.prototype', + 'rxjs/operator/startWith': 'Rx.Observable.prototype', + 'rxjs/operator/switchMap': 'Rx.Observable.prototype', + 'rxjs/operator/takeUntil': 'Rx.Observable.prototype', + 'rxjs/operator/toPromise': 'Rx.Observable.prototype', }; export type BundleConfig = {