From 4c8ea12903067a2ae281e9079f9ad7395e25bb44 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Fri, 17 Jul 2015 13:26:26 -0700 Subject: [PATCH] feat(pipes): changed .append to .extend BREAKING CHANGE: Pipes.append has been renamed into Pipes.extend. Pipes.extend prepends pipe factories instead of appending them. --- .../src/change_detection/pipes/pipes.ts | 54 ++++++------ .../test/change_detection/pipes/pipes_spec.ts | 85 +++++++++++++------ 2 files changed, 86 insertions(+), 53 deletions(-) diff --git a/modules/angular2/src/change_detection/pipes/pipes.ts b/modules/angular2/src/change_detection/pipes/pipes.ts index 068055d479..32cacaf843 100644 --- a/modules/angular2/src/change_detection/pipes/pipes.ts +++ b/modules/angular2/src/change_detection/pipes/pipes.ts @@ -40,18 +40,18 @@ export class Pipes { } /** - * Takes a {@link Pipes} config object and returns a binding used to append the - * provided config to an inherited {@link Pipes} instance and return a new + * Takes a {@link Pipes} config object and returns a binding used to extend the + * inherited {@link Pipes} instance with the provided config and return a new * {@link Pipes} instance. * * If the provided config contains a key that is not yet present in the * inherited {@link Pipes}' config, a new {@link PipeFactory} list will be created * for that key. Otherwise, the provided config will be merged with the inherited - * {@link Pipes} instance by appending pipes to their respective keys, without mutating + * {@link Pipes} instance by prepending pipes to their respective keys, without mutating * the inherited {@link Pipes}. * - * The following example shows how to append a new {@link PipeFactory} to the - * existing list of `async` factories, which will only be applied to the injector + * The following example shows how to extend an existing list of `async` factories + * with a new {@link PipeFactory}, which will only be applied to the injector * for this component and its children. This step is all that's required to make a new * pipe available to this component's template. * @@ -60,44 +60,42 @@ export class Pipes { * ``` * @Component({ * viewInjector: [ - * Pipes.append({ + * Pipes.extend({ * async: [newAsyncPipe] * }) * ] * }) * ``` */ - static append(config): Binding { + static extend(config): Binding { return new Binding(Pipes, { toFactory: (pipes: Pipes) => { - if (!isPresent(pipes)) { - // Typically would occur when calling Pipe.append inside of dependencies passed to - // bootstrap(), which would override default pipes instead of append. - throw new BaseException('Cannot append to Pipes without a parent injector'); + if (isBlank(pipes)) { + // Typically would occur when calling Pipe.extend inside of dependencies passed to + // bootstrap(), which would override default pipes instead of extending them. + throw new BaseException('Cannot extend Pipes without a parent injector'); } - var mergedConfig: StringMap = >{}; - - // Manual deep copy of existing Pipes config, - // so that lists of PipeFactories don't get mutated. - StringMapWrapper.forEach(pipes.config, (v: PipeFactory[], k: string) => { - var localPipeList: PipeFactory[] = mergedConfig[k] = []; - v.forEach((p: PipeFactory) => { localPipeList.push(p); }); - }); - - StringMapWrapper.forEach(config, (v: PipeFactory[], k: string) => { - if (isListLikeIterable(mergedConfig[k])) { - mergedConfig[k] = ListWrapper.concat(mergedConfig[k], config[k]); - } else { - mergedConfig[k] = config[k]; - } - }); - return new Pipes(mergedConfig); + return Pipes.create(config, pipes); }, // Dependency technically isn't optional, but we can provide a better error message this way. deps: [[Pipes, new UnboundedMetadata(), new OptionalMetadata()]] }); } + static create(config, pipes: Pipes = null): Pipes { + if (isPresent(pipes)) { + StringMapWrapper.forEach(pipes.config, (v: PipeFactory[], k: string) => { + if (StringMapWrapper.contains(config, k)) { + var configFactories: PipeFactory[] = config[k]; + config[k] = configFactories.concat(v); + } else { + config[k] = ListWrapper.clone(v); + } + }); + } + return new Pipes(config); + } + private _getListOfFactories(type: string, obj: any): PipeFactory[] { var listOfFactories = this.config[type]; if (isBlank(listOfFactories)) { diff --git a/modules/angular2/test/change_detection/pipes/pipes_spec.ts b/modules/angular2/test/change_detection/pipes/pipes_spec.ts index a6570d2e51..27fbc0d070 100644 --- a/modules/angular2/test/change_detection/pipes/pipes_spec.ts +++ b/modules/angular2/test/change_detection/pipes/pipes_spec.ts @@ -76,13 +76,47 @@ export function main() { .toThrowError(`Cannot find 'type' pipe supporting object 'some object'`); }); - describe('.append()', () => { - it('should create a factory that appends new pipes to old', () => { - firstPipeFactory.spy("supports").andReturn(false); + describe('.create()', () => { + it("should create a new Pipes object", () => { + firstPipeFactory.spy("supports").andReturn(true); + firstPipeFactory.spy("create").andReturn(firstPipe); + + var pipes = Pipes.create({'async': [firstPipeFactory]}); + expect(pipes.get("async", "first")).toBe(firstPipe); + }); + + it("should prepend passed it config in existing registry", () => { + firstPipeFactory.spy("supports").andReturn(true); secondPipeFactory.spy("supports").andReturn(true); secondPipeFactory.spy("create").andReturn(secondPipe); + + var pipes1 = Pipes.create({'async': [firstPipeFactory]}); + var pipes2 = Pipes.create({'async': [secondPipeFactory]}, pipes1); + + expect(pipes2.get("async", "first")).toBe(secondPipe); + }); + + it("should use inherited pipes when no overrides support the provided object", () => { + firstPipeFactory.spy("supports").andReturn(true); + firstPipeFactory.spy("create").andReturn(firstPipe); + secondPipeFactory.spy("supports").andReturn(false); + + var pipes1 = Pipes.create({'async': [firstPipeFactory], 'date': [firstPipeFactory]}); + var pipes2 = Pipes.create({'async': [secondPipeFactory]}, pipes1); + + expect(pipes2.get("async", "first")).toBe(firstPipe); + expect(pipes2.get("date", "first")).toBe(firstPipe); + }); + }); + + describe(".extend()", () => { + it('should create a factory that prepend new pipes to old', () => { + firstPipeFactory.spy("supports").andReturn(true); + secondPipeFactory.spy("supports").andReturn(true); + secondPipeFactory.spy("create").andReturn(secondPipe); + var originalPipes = new Pipes({'async': [firstPipeFactory]}); - var binding = Pipes.append({'async':[secondPipeFactory]}); + var binding = Pipes.extend({'async':[secondPipeFactory]}); var pipes: Pipes = binding.toFactory(originalPipes); expect(pipes.config['async'].length).toBe(2); @@ -90,33 +124,34 @@ export function main() { expect(pipes.get('async', 'second plz')).toBe(secondPipe); }); - - it('should append to di-inherited pipes', () => { - firstPipeFactory.spy("supports").andReturn(false); - secondPipeFactory.spy("supports").andReturn(true); - secondPipeFactory.spy("create").andReturn(secondPipe); - - var originalPipes: Pipes = new Pipes({'async': [firstPipeFactory]}); - var injector: Injector = Injector.resolveAndCreate([bind(Pipes).toValue(originalPipes)]); - var childInjector: Injector = - injector.resolveAndCreateChild([Pipes.append({'async': [secondPipeFactory]})]); - var parentPipes: Pipes = injector.get(Pipes); - var childPipes: Pipes = childInjector.get(Pipes); - expect(childPipes.config['async'].length).toBe(2); - expect(parentPipes.config['async'].length).toBe(1); - expect(childPipes.get('async', 'second plz')).toBe(secondPipe); - }); - - - it('should throw if calling append when creating root injector', () => { + it('should throw if calling extend when creating root injector', () => { secondPipeFactory.spy("supports").andReturn(true); secondPipeFactory.spy("create").andReturn(secondPipe); var injector: Injector = - Injector.resolveAndCreate([Pipes.append({'async': [secondPipeFactory]})]); + Injector.resolveAndCreate([Pipes.extend({'async': [secondPipeFactory]})]); expect(() => injector.get(Pipes)) - .toThrowError(/Cannot append to Pipes without a parent injector/g); + .toThrowError(/Cannot extend Pipes without a parent injector/g); + }); + + it('should extend di-inherited pipes', () => { + firstPipeFactory.spy("supports").andReturn(true); + firstPipeFactory.spy("create").andReturn(firstPipe); + + secondPipeFactory.spy("supports").andReturn(false); + + var originalPipes: Pipes = new Pipes({'async': [firstPipeFactory]}); + var injector: Injector = Injector.resolveAndCreate([bind(Pipes).toValue(originalPipes)]); + var childInjector: Injector = + injector.resolveAndCreateChild([Pipes.extend({'async': [secondPipeFactory]})]); + + var parentPipes: Pipes = injector.get(Pipes); + var childPipes: Pipes = childInjector.get(Pipes); + + expect(childPipes.config['async'].length).toBe(2); + expect(parentPipes.config['async'].length).toBe(1); + expect(childPipes.get('async', 'second plz')).toBe(firstPipe); }); }); });