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 ffee532f5328..47816e0ba66b 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,23 +209,23 @@ export class MdAutocompleteTrigger implements ControlValueAccessor, OnDestroy { /** Stream of clicks outside of the autocomplete panel. */ private get _outsideClickStream(): Observable { - if (this._document) { - return Observable.merge( - Observable.fromEvent(this._document, 'click'), - Observable.fromEvent(this._document, 'touchend') - ).filter((event: MouseEvent | TouchEvent) => { - 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 RxChain.from(merge( + fromEvent(this._document, 'click'), + fromEvent(this._document, 'touchend') + )).call(filter, (event: MouseEvent | TouchEvent) => { + 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)); + }).result(); } /** @@ -335,17 +334,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 cb0926192a2c..699b1f6bd568 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', () => { @@ -1335,10 +1335,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(); } } @@ -1444,10 +1447,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 8186adc63154..65722efbe73b 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', () => { @@ -337,7 +339,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 cb554c136f30..dd91042360ba 100644 --- a/src/lib/core/data-table/data-table.ts +++ b/src/lib/core/data-table/data-table.ts @@ -29,11 +29,9 @@ import { } from '@angular/core'; import {CollectionViewer, DataSource} from './data-source'; import {CdkCellOutlet, CdkCellOutletRowContext, CdkHeaderRowDef, CdkRowDef} from './row'; -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'; import {CdkCellDef, CdkColumnDef, CdkHeaderCellDef} from './cell'; @@ -180,22 +178,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() { @@ -233,12 +229,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..a6f22e575d8e --- /dev/null +++ b/src/lib/core/rxjs/index.ts @@ -0,0 +1,10 @@ +/** + * @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 + */ + +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..e9bc10ec5401 --- /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..80e2dfc43c7c --- /dev/null +++ b/src/lib/core/rxjs/rx-chain.ts @@ -0,0 +1,57 @@ +/** + * @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. + * + * This class is the concrete implementation, but the type used by the user when chaining + * is StrictRxChain. The strict chain enforces types on the operators to the same level as + * the prototype-added equivalents. + * + * @docs-private + */ +export class RxChain { + private 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..5cdf57be21da --- /dev/null +++ b/src/lib/core/rxjs/rx-operators.ts @@ -0,0 +1,154 @@ +/** + * @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. + * + * We achieve strict type enforcement on the chained operators by creating types that + * *unambiguously* match specific rxjs operators. These unambiguous types are created by + * intersecting a "brand" to the `typeof` the existing operator. The brand (a class with a private + * member) effectively forces nominal typing for the operators. This allows typescript to understand + * that, for example, `filter` is *`filter`* and not, say, a map of T => boolean. + * + * The downside to this approach is that operators must be imported in their type-coerced form + * rather than from the normal rxjs location. + * + * @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 917e2c4c603a..6bcfc1050110 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 642fae370e35..f29da1ed4c3a 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 = {