forked from angular/components
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
refactor: avoid polluting the user's rxjs setup
Currently, the internal uses of RxJS operators are done via patch imports, which add methods directly to the global `Observable` object. This can be an issue since the user may not know where these methods are coming from and may depend on them. Afterwards, if Material removes a method import, the user's app will break since the methods won't be defined anymore. This PR: * Replaces all of the patch imports with imports of individual operators and fixes any issues that showed up afterwards. * Adds a `FunctionChain` class to help with chaining the individually-imported operators. Fixes angular#2622.
- Loading branch information
Showing
18 changed files
with
277 additions
and
126 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
import {FunctionChain} from './function-chain'; | ||
|
||
describe('FunctionChain', () => { | ||
it('should chain functions', () => { | ||
let first = jasmine.createSpy('First function'); | ||
let second = jasmine.createSpy('Second function'); | ||
let third = jasmine.createSpy('Third function'); | ||
|
||
new FunctionChain().call(first).call(second).call(third).execute(); | ||
|
||
expect(first).toHaveBeenCalled(); | ||
expect(second).toHaveBeenCalled(); | ||
expect(third).toHaveBeenCalled(); | ||
}); | ||
|
||
it('should pass in arguments to the functions', () => { | ||
let first = jasmine.createSpy('First function'); | ||
let second = jasmine.createSpy('Second function'); | ||
|
||
new FunctionChain().call(first, 1, 2).call(second, 3, 4).execute(); | ||
|
||
expect(first).toHaveBeenCalledWith(1, 2); | ||
expect(second).toHaveBeenCalledWith(3, 4); | ||
}); | ||
|
||
it('should clear the chain once it has been executed', () => { | ||
let fn = jasmine.createSpy('Spy function'); | ||
let chain = new FunctionChain().call(fn); | ||
|
||
chain.execute(); | ||
expect(fn).toHaveBeenCalledTimes(1); | ||
|
||
chain.execute(); | ||
expect(fn).toHaveBeenCalledTimes(1); | ||
}); | ||
|
||
it('should return the final function result', () => { | ||
let result = new FunctionChain() | ||
.context([1, 2, 3, 4, 5]) | ||
.call(Array.prototype.map, (current: number) => current * 2) | ||
.call(Array.prototype.filter, (current: number) => current > 5) | ||
.execute(); | ||
|
||
expect(result).toEqual([6, 8, 10]); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* Collects and executes a chain of functions. Useful in cases like RxJS, where we can't chain | ||
* the methods directly, but rather have to call them individually. | ||
* @docs-private | ||
* | ||
* @example | ||
* // Standard way | ||
* someObservable.filter(...).map(...).do(...); | ||
* | ||
* // Function chain | ||
* new FunctionChain() | ||
* .context(someObservable) | ||
* .call(filter, ...) | ||
* .call(map, ...) | ||
* .call(do, ...) | ||
* .execute(); | ||
*/ | ||
export class FunctionChain<T> { | ||
/** Tracks the currently-chained functions. */ | ||
private _chainedCalls: any[] = []; | ||
|
||
constructor(private _initialContext?: any) { } | ||
|
||
/** | ||
* Adds a function to the chain. | ||
* @param fn Functions to be added to the chain. | ||
* @param ...args Arguments with which to call the function. | ||
*/ | ||
call(fn: Function, ...args: any[]): this { | ||
this._chainedCalls.push([fn, ...args]); | ||
return this; | ||
} | ||
|
||
/** | ||
* Executes all of the functions in the chain and clears it. | ||
* @returns The return value of the final function in the chain. | ||
*/ | ||
execute(): T { | ||
let result = this._chainedCalls.reduce((currentValue, currentFunction) => { | ||
return (currentFunction.shift() as Function).apply(currentValue, currentFunction); | ||
}, this._initialContext); | ||
|
||
this._chainedCalls.length = 0; | ||
|
||
return result as T; | ||
} | ||
|
||
/** | ||
* Allows setting the initial context in a separate call from the constructor. | ||
* This helps with readability where the line with the constructor could be too long. | ||
* @param initialContext The new initial context. | ||
*/ | ||
context(initialContext: any): this { | ||
this._initialContext = initialContext; | ||
return this; | ||
} | ||
} |
Oops, something went wrong.