From 8d85b839b62455032245b68415edca45b8debe76 Mon Sep 17 00:00:00 2001 From: vsavkin Date: Fri, 27 Feb 2015 13:38:25 -0800 Subject: [PATCH] feat(change_detection): pass binding propagation config to pipe registry --- modules/angular2/change_detection.js | 2 ++ modules/angular2/core.js | 1 - .../abstract_change_detector.js | 3 +++ .../binding_propagation_config.js | 2 +- .../change_detection_jit_generator.es6 | 20 ++++++++++--------- .../dynamic_change_detector.js | 14 ++++++++++--- .../src/change_detection/parser/ast.js | 4 +++- .../src/change_detection/parser/parser.js | 4 ++-- .../pipes/iterable_changes.js | 2 +- .../pipes/keyvalue_changes.js | 2 +- .../src/change_detection/pipes/null_pipe.js | 2 +- .../change_detection/pipes/pipe_registry.js | 5 +++-- .../change_detection/proto_change_detector.js | 4 +++- .../src/change_detection/proto_record.js | 3 ++- .../src/core/compiler/element_injector.js | 2 +- modules/angular2/src/core/compiler/view.js | 3 +-- .../change_detection/change_detection_spec.js | 16 ++++++++++++++- .../change_detection/pipe_registry_spec.js | 8 ++++---- .../core/compiler/element_injector_spec.js | 2 +- .../test/core/compiler/integration_spec.js | 5 ++--- .../pipeline/element_binder_builder_spec.js | 2 +- 21 files changed, 69 insertions(+), 37 deletions(-) rename modules/angular2/src/{core/compiler => change_detection}/binding_propagation_config.js (94%) diff --git a/modules/angular2/change_detection.js b/modules/angular2/change_detection.js index 3583c76f1d..f67126396c 100644 --- a/modules/angular2/change_detection.js +++ b/modules/angular2/change_detection.js @@ -11,6 +11,8 @@ export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector, from './src/change_detection/proto_change_detector'; export {DynamicChangeDetector} from './src/change_detection/dynamic_change_detector'; +export {BindingPropagationConfig} + from './src/change_detection/binding_propagation_config'; export * from './src/change_detection/pipes/pipe_registry'; export {uninitialized} from './src/change_detection/change_detection_util'; export * from './src/change_detection/pipes/pipe'; diff --git a/modules/angular2/core.js b/modules/angular2/core.js index 639df4aa46..e5aa7f5b96 100644 --- a/modules/angular2/core.js +++ b/modules/angular2/core.js @@ -9,6 +9,5 @@ export * from './src/core/compiler/compiler'; export * from './src/core/compiler/template_loader'; export * from './src/core/compiler/view'; export * from './src/core/compiler/view_container'; -export * from './src/core/compiler/binding_propagation_config'; export * from './src/core/dom/element'; diff --git a/modules/angular2/src/change_detection/abstract_change_detector.js b/modules/angular2/src/change_detection/abstract_change_detector.js index a5274e1680..e223259e61 100644 --- a/modules/angular2/src/change_detection/abstract_change_detector.js +++ b/modules/angular2/src/change_detection/abstract_change_detector.js @@ -1,15 +1,18 @@ import {isPresent} from 'angular2/src/facade/lang'; import {List, ListWrapper} from 'angular2/src/facade/collection'; +import {BindingPropagationConfig} from './binding_propagation_config'; import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces'; export class AbstractChangeDetector extends ChangeDetector { children:List; parent:ChangeDetector; mode:string; + bindingPropagationConfig:BindingPropagationConfig; constructor() { super(); this.children = []; + this.bindingPropagationConfig = new BindingPropagationConfig(this); this.mode = CHECK_ALWAYS; } diff --git a/modules/angular2/src/core/compiler/binding_propagation_config.js b/modules/angular2/src/change_detection/binding_propagation_config.js similarity index 94% rename from modules/angular2/src/core/compiler/binding_propagation_config.js rename to modules/angular2/src/change_detection/binding_propagation_config.js index 2c76c81c4b..b2a0535144 100644 --- a/modules/angular2/src/core/compiler/binding_propagation_config.js +++ b/modules/angular2/src/change_detection/binding_propagation_config.js @@ -1,4 +1,4 @@ -import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from 'angular2/change_detection'; +import {ChangeDetector, CHECK_ONCE, DETACHED, CHECK_ALWAYS} from './interfaces'; /** * @publicModule angular2/angular2 diff --git a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 index 3ec631528b..f4bbb79a85 100644 --- a/modules/angular2/src/change_detection/change_detection_jit_generator.es6 +++ b/modules/angular2/src/change_detection/change_detection_jit_generator.es6 @@ -15,6 +15,7 @@ import { RECORD_TYPE_PRIMITIVE_OP, RECORD_TYPE_KEYED_ACCESS, RECORD_TYPE_PIPE, + RECORD_TYPE_BINDING_PIPE, RECORD_TYPE_INTERPOLATE } from './proto_record'; @@ -180,14 +181,14 @@ if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) { `; } -function pipeCheckTemplate(context:string, pipe:string, pipeType:string, +function pipeCheckTemplate(context:string, bindingPropagationConfig:string, pipe:string, pipeType:string, value:string, change:string, addRecord:string, notify:string):string{ return ` if (${pipe} === ${UTIL}.unitialized()) { - ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}); + ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig}); } else if (!${pipe}.supports(${context})) { ${pipe}.onDestroy(); - ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}); + ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig}); } ${CHANGE_LOCAL} = ${pipe}.transform(${context}); @@ -293,20 +294,20 @@ export class ChangeDetectorJITGenerator { genHydrate():string { return hydrateTemplate(this.typeName, this.genFieldDefinitions(), - pipeOnDestroyTemplate(this.getnonNullPipeNames())); + pipeOnDestroyTemplate(this.getNonNullPipeNames())); } genFieldDefinitions() { var fields = []; fields = fields.concat(this.fieldNames); - fields = fields.concat(this.getnonNullPipeNames()); + fields = fields.concat(this.getNonNullPipeNames()); return fieldDefinitionsTemplate(fields); } - getnonNullPipeNames():List { + getNonNullPipeNames():List { var pipes = []; this.records.forEach((r) => { - if (r.mode === RECORD_TYPE_PIPE) { + if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) { pipes.push(this.pipeNames[r.selfIndex]); } }); @@ -332,7 +333,7 @@ export class ChangeDetectorJITGenerator { } genRecord(r:ProtoRecord):string { - if (r.mode === RECORD_TYPE_PIPE) { + if (r.mode === RECORD_TYPE_PIPE || r.mode === RECORD_TYPE_BINDING_PIPE) { return this.genPipeCheck (r); } else { return this.genReferenceCheck(r); @@ -345,11 +346,12 @@ export class ChangeDetectorJITGenerator { var newValue = this.localNames[r.selfIndex]; var oldValue = this.fieldNames[r.selfIndex]; var change = this.changeNames[r.selfIndex]; + var bpc = r.mode === RECORD_TYPE_BINDING_PIPE ? "this.bindingPropagationConfig" : "null"; var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue); var notify = this.genNotify(r); - return pipeCheckTemplate(context, pipe, r.name, newValue, change, addRecord, notify); + return pipeCheckTemplate(context, bpc, pipe, r.name, newValue, change, addRecord, notify); } genReferenceCheck(r:ProtoRecord):string { diff --git a/modules/angular2/src/change_detection/dynamic_change_detector.js b/modules/angular2/src/change_detection/dynamic_change_detector.js index 3cd598a3da..6c98ca4b3d 100644 --- a/modules/angular2/src/change_detection/dynamic_change_detector.js +++ b/modules/angular2/src/change_detection/dynamic_change_detector.js @@ -3,7 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca import {AbstractChangeDetector} from './abstract_change_detector'; import {PipeRegistry} from './pipes/pipe_registry'; -import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util'; +import {ChangeDetectionUtil, uninitialized} from './change_detection_util'; import { @@ -17,6 +17,7 @@ import { RECORD_TYPE_PRIMITIVE_OP, RECORD_TYPE_KEYED_ACCESS, RECORD_TYPE_PIPE, + RECORD_TYPE_BINDING_PIPE, RECORD_TYPE_INTERPOLATE } from './proto_record'; @@ -103,7 +104,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector { _check(proto:ProtoRecord) { try { - if (proto.mode == RECORD_TYPE_PIPE) { + if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) { return this._pipeCheck(proto); } else { return this._referenceCheck(proto); @@ -202,7 +203,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector { if (isPresent(storedPipe)) { storedPipe.onDestroy(); } - var pipe = this.pipeRegistry.get(proto.name, context); + + // Currently, only pipes that used in bindings in the template get + // the bindingPropagationConfig of the encompassing component. + // + // In the future, pipes declared in the bind configuration should + // be able to access the bindingPropagationConfig of that component. + var bpc = proto.mode === RECORD_TYPE_BINDING_PIPE ? this.bindingPropagationConfig : null; + var pipe = this.pipeRegistry.get(proto.name, context, bpc); this._writePipe(proto, pipe); return pipe; } diff --git a/modules/angular2/src/change_detection/parser/ast.js b/modules/angular2/src/change_detection/parser/ast.js index 96ffc12b54..db8cde892e 100644 --- a/modules/angular2/src/change_detection/parser/ast.js +++ b/modules/angular2/src/change_detection/parser/ast.js @@ -168,11 +168,13 @@ export class Pipe extends AST { exp:AST; name:string; args:List; - constructor(exp:AST, name:string, args:List) { + inBinding:boolean; + constructor(exp:AST, name:string, args:List, inBinding:boolean) { super(); this.exp = exp; this.name = name; this.args = args; + this.inBinding = inBinding; } visit(visitor) { diff --git a/modules/angular2/src/change_detection/parser/parser.js b/modules/angular2/src/change_detection/parser/parser.js index a67fd493c2..463593f80e 100644 --- a/modules/angular2/src/change_detection/parser/parser.js +++ b/modules/angular2/src/change_detection/parser/parser.js @@ -58,7 +58,7 @@ export class Parser { if (ListWrapper.isEmpty(pipes)) return bindingAst; var res = ListWrapper.reduce(pipes, - (result, currentPipeName) => new Pipe(result, currentPipeName, []), + (result, currentPipeName) => new Pipe(result, currentPipeName, [], false), bindingAst.ast); return new ASTWithSource(res, bindingAst.source, bindingAst.location); } @@ -220,7 +220,7 @@ class _ParseAST { while (this.optionalCharacter($COLON)) { ListWrapper.push(args, this.parseExpression()); } - result = new Pipe(result, name, args); + result = new Pipe(result, name, args, true); } return result; } diff --git a/modules/angular2/src/change_detection/pipes/iterable_changes.js b/modules/angular2/src/change_detection/pipes/iterable_changes.js index 2469aa9e09..1baaed2afb 100644 --- a/modules/angular2/src/change_detection/pipes/iterable_changes.js +++ b/modules/angular2/src/change_detection/pipes/iterable_changes.js @@ -21,7 +21,7 @@ export class IterableChangesFactory { return IterableChanges.supportsObj(obj); } - create():Pipe { + create(bpc):Pipe { return new IterableChanges(); } } diff --git a/modules/angular2/src/change_detection/pipes/keyvalue_changes.js b/modules/angular2/src/change_detection/pipes/keyvalue_changes.js index 0662ec359e..0eb9d2193b 100644 --- a/modules/angular2/src/change_detection/pipes/keyvalue_changes.js +++ b/modules/angular2/src/change_detection/pipes/keyvalue_changes.js @@ -8,7 +8,7 @@ export class KeyValueChangesFactory { return KeyValueChanges.supportsObj(obj); } - create():Pipe { + create(bpc):Pipe { return new KeyValueChanges(); } } diff --git a/modules/angular2/src/change_detection/pipes/null_pipe.js b/modules/angular2/src/change_detection/pipes/null_pipe.js index 1055619ff3..2648682cb2 100644 --- a/modules/angular2/src/change_detection/pipes/null_pipe.js +++ b/modules/angular2/src/change_detection/pipes/null_pipe.js @@ -6,7 +6,7 @@ export class NullPipeFactory { return NullPipe.supportsObj(obj); } - create():Pipe { + create(bpc):Pipe { return new NullPipe(); } } diff --git a/modules/angular2/src/change_detection/pipes/pipe_registry.js b/modules/angular2/src/change_detection/pipes/pipe_registry.js index f4d1a7b2c3..3674b7d03e 100644 --- a/modules/angular2/src/change_detection/pipes/pipe_registry.js +++ b/modules/angular2/src/change_detection/pipes/pipe_registry.js @@ -1,6 +1,7 @@ import {List, ListWrapper} from 'angular2/src/facade/collection'; import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang'; import {Pipe} from './pipe'; +import {BindingPropagationConfig} from '../binding_propagation_config'; export class PipeRegistry { config; @@ -9,7 +10,7 @@ export class PipeRegistry { this.config = config; } - get(type:string, obj):Pipe { + get(type:string, obj, bpc:BindingPropagationConfig):Pipe { var listOfConfigs = this.config[type]; if (isBlank(listOfConfigs)) { throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`); @@ -22,6 +23,6 @@ export class PipeRegistry { throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`); } - return matchingConfig.create(); + return matchingConfig.create(bpc); } } \ No newline at end of file diff --git a/modules/angular2/src/change_detection/proto_change_detector.js b/modules/angular2/src/change_detection/proto_change_detector.js index 7349e13c3b..cdefbd87ea 100644 --- a/modules/angular2/src/change_detection/proto_change_detector.js +++ b/modules/angular2/src/change_detection/proto_change_detector.js @@ -41,6 +41,7 @@ import { RECORD_TYPE_PRIMITIVE_OP, RECORD_TYPE_KEYED_ACCESS, RECORD_TYPE_PIPE, + RECORD_TYPE_BINDING_PIPE, RECORD_TYPE_INTERPOLATE } from './proto_record'; @@ -239,7 +240,8 @@ class _ConvertAstIntoProtoRecords { visitPipe(ast:Pipe) { var value = ast.exp.visit(this); - return this._addRecord(RECORD_TYPE_PIPE, ast.name, ast.name, [], null, value); + var type = ast.inBinding ? RECORD_TYPE_BINDING_PIPE : RECORD_TYPE_PIPE; + return this._addRecord(type, ast.name, ast.name, [], null, value); } visitKeyedAccess(ast:KeyedAccess) { diff --git a/modules/angular2/src/change_detection/proto_record.js b/modules/angular2/src/change_detection/proto_record.js index 357aec7422..3b77fca8ac 100644 --- a/modules/angular2/src/change_detection/proto_record.js +++ b/modules/angular2/src/change_detection/proto_record.js @@ -9,7 +9,8 @@ export const RECORD_TYPE_INVOKE_METHOD = 5; export const RECORD_TYPE_INVOKE_CLOSURE = 6; export const RECORD_TYPE_KEYED_ACCESS = 7; export const RECORD_TYPE_PIPE = 8; -export const RECORD_TYPE_INTERPOLATE = 9; +export const RECORD_TYPE_BINDING_PIPE = 9; +export const RECORD_TYPE_INTERPOLATE = 10; export class ProtoRecord { mode:number; diff --git a/modules/angular2/src/core/compiler/element_injector.js b/modules/angular2/src/core/compiler/element_injector.js index e723fbfc78..fa5408af0a 100644 --- a/modules/angular2/src/core/compiler/element_injector.js +++ b/modules/angular2/src/core/compiler/element_injector.js @@ -8,7 +8,7 @@ import * as viewModule from 'angular2/src/core/compiler/view'; import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {NgElement} from 'angular2/src/core/dom/element'; import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'; -import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'; +import {BindingPropagationConfig} from 'angular2/change_detection'; import * as pclModule from 'angular2/src/core/compiler/private_component_location'; import {setterFactory} from './property_setter_factory'; diff --git a/modules/angular2/src/core/compiler/view.js b/modules/angular2/src/core/compiler/view.js index b5859846ee..f0e7849ec4 100644 --- a/modules/angular2/src/core/compiler/view.js +++ b/modules/angular2/src/core/compiler/view.js @@ -2,10 +2,9 @@ import {DOM} from 'angular2/src/dom/dom_adapter'; import {Promise} from 'angular2/src/facade/async'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, - ChangeRecord, BindingRecord, uninitialized} from 'angular2/change_detection'; + ChangeRecord, BindingRecord, BindingPropagationConfig, uninitialized} from 'angular2/change_detection'; import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector'; -import {BindingPropagationConfig} from './binding_propagation_config'; import {ElementBinder} from './element_binder'; import {DirectiveMetadata} from './directive_metadata'; import {SetterFn} from 'angular2/src/reflection/types'; diff --git a/modules/angular2/test/change_detection/change_detection_spec.js b/modules/angular2/test/change_detection/change_detection_spec.js index 0a90508380..eb9648b068 100644 --- a/modules/angular2/test/change_detection/change_detection_spec.js +++ b/modules/angular2/test/change_detection/change_detection_spec.js @@ -535,6 +535,18 @@ export function main() { expect(pipe.destroyCalled).toEqual(true); }); + + it("should inject the binding propagation configuration " + + "of the encompassing component into a pipe", () => { + + var registry = new FakePipeRegistry('pipe', () => new IdentityPipe()); + var c = createChangeDetector("memo", "name | pipe", new Person('bob'), null, registry); + var cd = c["changeDetector"]; + + cd.detectChanges(); + + expect(registry.bpc).toBe(cd.bindingPropagationConfig); + }); }); it("should do nothing when returns NO_CHANGE", () => { @@ -622,6 +634,7 @@ class FakePipeRegistry extends PipeRegistry { numberOfLookups:number; pipeType:string; factory:Function; + bpc:any; constructor(pipeType, factory) { super({}); @@ -630,9 +643,10 @@ class FakePipeRegistry extends PipeRegistry { this.numberOfLookups = 0; } - get(type:string, obj) { + get(type:string, obj, bpc) { if (type != this.pipeType) return null; this.numberOfLookups ++; + this.bpc = bpc; return this.factory(); } } diff --git a/modules/angular2/test/change_detection/pipe_registry_spec.js b/modules/angular2/test/change_detection/pipe_registry_spec.js index 3c0d9d4cc4..76ee6efa8e 100644 --- a/modules/angular2/test/change_detection/pipe_registry_spec.js +++ b/modules/angular2/test/change_detection/pipe_registry_spec.js @@ -16,12 +16,12 @@ export function main() { ] }); - expect(r.get("type", "some object")).toBe(secondPipe); + expect(r.get("type", "some object", null)).toBe(secondPipe); }); it("should throw when no matching type", () => { var r = new PipeRegistry({}); - expect(() => r.get("unknown", "some object")).toThrowError( + expect(() => r.get("unknown", "some object", null)).toThrowError( `Cannot find a pipe for type 'unknown' object 'some object'` ); }); @@ -31,7 +31,7 @@ export function main() { "type" : [] }); - expect(() => r.get("type", "some object")).toThrowError( + expect(() => r.get("type", "some object", null)).toThrowError( `Cannot find a pipe for type 'type' object 'some object'` ); }); @@ -51,7 +51,7 @@ class PipeFactory { return this.shouldSupport; } - create():Pipe { + create(bpc):Pipe { return this.pipe; } } \ No newline at end of file diff --git a/modules/angular2/test/core/compiler/element_injector_spec.js b/modules/angular2/test/core/compiler/element_injector_spec.js index 93f54bc16a..f28e171b4f 100644 --- a/modules/angular2/test/core/compiler/element_injector_spec.js +++ b/modules/angular2/test/core/compiler/element_injector_spec.js @@ -12,7 +12,7 @@ import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {NgElement} from 'angular2/src/core/dom/element'; import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; import {Directive} from 'angular2/src/core/annotations/annotations'; -import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'; +import {BindingPropagationConfig} from 'angular2/change_detection'; @proxy @IMPLEMENTS(View) diff --git a/modules/angular2/test/core/compiler/integration_spec.js b/modules/angular2/test/core/compiler/integration_spec.js index 216eb6b798..98a59b77b4 100644 --- a/modules/angular2/test/core/compiler/integration_spec.js +++ b/modules/angular2/test/core/compiler/integration_spec.js @@ -18,7 +18,7 @@ import {PromiseWrapper} from 'angular2/src/facade/async'; import {Injector, bind} from 'angular2/di'; import {Lexer, Parser, dynamicChangeDetection, - DynamicChangeDetection, Pipe, PipeRegistry} from 'angular2/change_detection'; + DynamicChangeDetection, Pipe, PipeRegistry, BindingPropagationConfig} from 'angular2/change_detection'; import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler'; import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; @@ -27,7 +27,6 @@ import {PrivateComponentLocation} from 'angular2/src/core/compiler/private_compo import {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader'; import {TemplateLoader} from 'angular2/src/core/compiler/template_loader'; import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; -import {BindingPropagationConfig} from 'angular2/src/core/compiler/binding_propagation_config'; import {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper'; import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver'; @@ -869,7 +868,7 @@ class DoublePipeFactory { return true; } - create() { + create(bpc) { return new DoublePipe(); } } diff --git a/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js b/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js index 62089aa5ca..07ecef7bbf 100644 --- a/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js +++ b/modules/angular2/test/core/compiler/pipeline/element_binder_builder_spec.js @@ -660,7 +660,7 @@ class DoublePipeFactory { return true; } - create() { + create(bpc) { return new DoublePipe(); } }