feat(change_detection): pass binding propagation config to pipe registry

This commit is contained in:
vsavkin 2015-02-27 13:38:25 -08:00
parent dd235f38a3
commit 8d85b839b6
21 changed files with 69 additions and 37 deletions

View File

@ -11,6 +11,8 @@ export {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector,
from './src/change_detection/proto_change_detector'; from './src/change_detection/proto_change_detector';
export {DynamicChangeDetector} export {DynamicChangeDetector}
from './src/change_detection/dynamic_change_detector'; 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 * from './src/change_detection/pipes/pipe_registry';
export {uninitialized} from './src/change_detection/change_detection_util'; export {uninitialized} from './src/change_detection/change_detection_util';
export * from './src/change_detection/pipes/pipe'; export * from './src/change_detection/pipes/pipe';

View File

@ -9,6 +9,5 @@ export * from './src/core/compiler/compiler';
export * from './src/core/compiler/template_loader'; export * from './src/core/compiler/template_loader';
export * from './src/core/compiler/view'; export * from './src/core/compiler/view';
export * from './src/core/compiler/view_container'; export * from './src/core/compiler/view_container';
export * from './src/core/compiler/binding_propagation_config';
export * from './src/core/dom/element'; export * from './src/core/dom/element';

View File

@ -1,15 +1,18 @@
import {isPresent} from 'angular2/src/facade/lang'; import {isPresent} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper} from 'angular2/src/facade/collection';
import {BindingPropagationConfig} from './binding_propagation_config';
import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces'; import {ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
export class AbstractChangeDetector extends ChangeDetector { export class AbstractChangeDetector extends ChangeDetector {
children:List; children:List;
parent:ChangeDetector; parent:ChangeDetector;
mode:string; mode:string;
bindingPropagationConfig:BindingPropagationConfig;
constructor() { constructor() {
super(); super();
this.children = []; this.children = [];
this.bindingPropagationConfig = new BindingPropagationConfig(this);
this.mode = CHECK_ALWAYS; this.mode = CHECK_ALWAYS;
} }

View File

@ -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 * @publicModule angular2/angular2

View File

@ -15,6 +15,7 @@ import {
RECORD_TYPE_PRIMITIVE_OP, RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS, RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE, RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE RECORD_TYPE_INTERPOLATE
} from './proto_record'; } 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{ value:string, change:string, addRecord:string, notify:string):string{
return ` return `
if (${pipe} === ${UTIL}.unitialized()) { if (${pipe} === ${UTIL}.unitialized()) {
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}); ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
} else if (!${pipe}.supports(${context})) { } else if (!${pipe}.supports(${context})) {
${pipe}.onDestroy(); ${pipe}.onDestroy();
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}); ${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('${pipeType}', ${context}, ${bindingPropagationConfig});
} }
${CHANGE_LOCAL} = ${pipe}.transform(${context}); ${CHANGE_LOCAL} = ${pipe}.transform(${context});
@ -293,20 +294,20 @@ export class ChangeDetectorJITGenerator {
genHydrate():string { genHydrate():string {
return hydrateTemplate(this.typeName, this.genFieldDefinitions(), return hydrateTemplate(this.typeName, this.genFieldDefinitions(),
pipeOnDestroyTemplate(this.getnonNullPipeNames())); pipeOnDestroyTemplate(this.getNonNullPipeNames()));
} }
genFieldDefinitions() { genFieldDefinitions() {
var fields = []; var fields = [];
fields = fields.concat(this.fieldNames); fields = fields.concat(this.fieldNames);
fields = fields.concat(this.getnonNullPipeNames()); fields = fields.concat(this.getNonNullPipeNames());
return fieldDefinitionsTemplate(fields); return fieldDefinitionsTemplate(fields);
} }
getnonNullPipeNames():List<String> { getNonNullPipeNames():List<String> {
var pipes = []; var pipes = [];
this.records.forEach((r) => { 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]); pipes.push(this.pipeNames[r.selfIndex]);
} }
}); });
@ -332,7 +333,7 @@ export class ChangeDetectorJITGenerator {
} }
genRecord(r:ProtoRecord):string { 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); return this.genPipeCheck (r);
} else { } else {
return this.genReferenceCheck(r); return this.genReferenceCheck(r);
@ -345,11 +346,12 @@ export class ChangeDetectorJITGenerator {
var newValue = this.localNames[r.selfIndex]; var newValue = this.localNames[r.selfIndex];
var oldValue = this.fieldNames[r.selfIndex]; var oldValue = this.fieldNames[r.selfIndex];
var change = this.changeNames[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 addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
var notify = this.genNotify(r); 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 { genReferenceCheck(r:ProtoRecord):string {

View File

@ -3,7 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
import {AbstractChangeDetector} from './abstract_change_detector'; import {AbstractChangeDetector} from './abstract_change_detector';
import {PipeRegistry} from './pipes/pipe_registry'; import {PipeRegistry} from './pipes/pipe_registry';
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util'; import {ChangeDetectionUtil, uninitialized} from './change_detection_util';
import { import {
@ -17,6 +17,7 @@ import {
RECORD_TYPE_PRIMITIVE_OP, RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS, RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE, RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE RECORD_TYPE_INTERPOLATE
} from './proto_record'; } from './proto_record';
@ -103,7 +104,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
_check(proto:ProtoRecord) { _check(proto:ProtoRecord) {
try { try {
if (proto.mode == RECORD_TYPE_PIPE) { if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
return this._pipeCheck(proto); return this._pipeCheck(proto);
} else { } else {
return this._referenceCheck(proto); return this._referenceCheck(proto);
@ -202,7 +203,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
if (isPresent(storedPipe)) { if (isPresent(storedPipe)) {
storedPipe.onDestroy(); 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); this._writePipe(proto, pipe);
return pipe; return pipe;
} }

View File

@ -168,11 +168,13 @@ export class Pipe extends AST {
exp:AST; exp:AST;
name:string; name:string;
args:List<AST>; args:List<AST>;
constructor(exp:AST, name:string, args:List) { inBinding:boolean;
constructor(exp:AST, name:string, args:List, inBinding:boolean) {
super(); super();
this.exp = exp; this.exp = exp;
this.name = name; this.name = name;
this.args = args; this.args = args;
this.inBinding = inBinding;
} }
visit(visitor) { visit(visitor) {

View File

@ -58,7 +58,7 @@ export class Parser {
if (ListWrapper.isEmpty(pipes)) return bindingAst; if (ListWrapper.isEmpty(pipes)) return bindingAst;
var res = ListWrapper.reduce(pipes, var res = ListWrapper.reduce(pipes,
(result, currentPipeName) => new Pipe(result, currentPipeName, []), (result, currentPipeName) => new Pipe(result, currentPipeName, [], false),
bindingAst.ast); bindingAst.ast);
return new ASTWithSource(res, bindingAst.source, bindingAst.location); return new ASTWithSource(res, bindingAst.source, bindingAst.location);
} }
@ -220,7 +220,7 @@ class _ParseAST {
while (this.optionalCharacter($COLON)) { while (this.optionalCharacter($COLON)) {
ListWrapper.push(args, this.parseExpression()); ListWrapper.push(args, this.parseExpression());
} }
result = new Pipe(result, name, args); result = new Pipe(result, name, args, true);
} }
return result; return result;
} }

View File

@ -21,7 +21,7 @@ export class IterableChangesFactory {
return IterableChanges.supportsObj(obj); return IterableChanges.supportsObj(obj);
} }
create():Pipe { create(bpc):Pipe {
return new IterableChanges(); return new IterableChanges();
} }
} }

View File

@ -8,7 +8,7 @@ export class KeyValueChangesFactory {
return KeyValueChanges.supportsObj(obj); return KeyValueChanges.supportsObj(obj);
} }
create():Pipe { create(bpc):Pipe {
return new KeyValueChanges(); return new KeyValueChanges();
} }
} }

View File

@ -6,7 +6,7 @@ export class NullPipeFactory {
return NullPipe.supportsObj(obj); return NullPipe.supportsObj(obj);
} }
create():Pipe { create(bpc):Pipe {
return new NullPipe(); return new NullPipe();
} }
} }

View File

@ -1,6 +1,7 @@
import {List, ListWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper} from 'angular2/src/facade/collection';
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang'; import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
import {Pipe} from './pipe'; import {Pipe} from './pipe';
import {BindingPropagationConfig} from '../binding_propagation_config';
export class PipeRegistry { export class PipeRegistry {
config; config;
@ -9,7 +10,7 @@ export class PipeRegistry {
this.config = config; this.config = config;
} }
get(type:string, obj):Pipe { get(type:string, obj, bpc:BindingPropagationConfig):Pipe {
var listOfConfigs = this.config[type]; var listOfConfigs = this.config[type];
if (isBlank(listOfConfigs)) { if (isBlank(listOfConfigs)) {
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`); 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}'`); throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
} }
return matchingConfig.create(); return matchingConfig.create(bpc);
} }
} }

View File

@ -41,6 +41,7 @@ import {
RECORD_TYPE_PRIMITIVE_OP, RECORD_TYPE_PRIMITIVE_OP,
RECORD_TYPE_KEYED_ACCESS, RECORD_TYPE_KEYED_ACCESS,
RECORD_TYPE_PIPE, RECORD_TYPE_PIPE,
RECORD_TYPE_BINDING_PIPE,
RECORD_TYPE_INTERPOLATE RECORD_TYPE_INTERPOLATE
} from './proto_record'; } from './proto_record';
@ -239,7 +240,8 @@ class _ConvertAstIntoProtoRecords {
visitPipe(ast:Pipe) { visitPipe(ast:Pipe) {
var value = ast.exp.visit(this); 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) { visitKeyedAccess(ast:KeyedAccess) {

View File

@ -9,7 +9,8 @@ export const RECORD_TYPE_INVOKE_METHOD = 5;
export const RECORD_TYPE_INVOKE_CLOSURE = 6; export const RECORD_TYPE_INVOKE_CLOSURE = 6;
export const RECORD_TYPE_KEYED_ACCESS = 7; export const RECORD_TYPE_KEYED_ACCESS = 7;
export const RECORD_TYPE_PIPE = 8; 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 { export class ProtoRecord {
mode:number; mode:number;

View File

@ -8,7 +8,7 @@ import * as viewModule from 'angular2/src/core/compiler/view';
import {ViewContainer} from 'angular2/src/core/compiler/view_container'; import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {NgElement} from 'angular2/src/core/dom/element'; import {NgElement} from 'angular2/src/core/dom/element';
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations'; 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 * as pclModule from 'angular2/src/core/compiler/private_component_location';
import {setterFactory} from './property_setter_factory'; import {setterFactory} from './property_setter_factory';

View File

@ -2,10 +2,9 @@ import {DOM} from 'angular2/src/dom/dom_adapter';
import {Promise} from 'angular2/src/facade/async'; import {Promise} from 'angular2/src/facade/async';
import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection'; import {ListWrapper, MapWrapper, Map, StringMapWrapper, List} from 'angular2/src/facade/collection';
import {AST, Locals, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, 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 {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
import {BindingPropagationConfig} from './binding_propagation_config';
import {ElementBinder} from './element_binder'; import {ElementBinder} from './element_binder';
import {DirectiveMetadata} from './directive_metadata'; import {DirectiveMetadata} from './directive_metadata';
import {SetterFn} from 'angular2/src/reflection/types'; import {SetterFn} from 'angular2/src/reflection/types';

View File

@ -535,6 +535,18 @@ export function main() {
expect(pipe.destroyCalled).toEqual(true); 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", () => { it("should do nothing when returns NO_CHANGE", () => {
@ -622,6 +634,7 @@ class FakePipeRegistry extends PipeRegistry {
numberOfLookups:number; numberOfLookups:number;
pipeType:string; pipeType:string;
factory:Function; factory:Function;
bpc:any;
constructor(pipeType, factory) { constructor(pipeType, factory) {
super({}); super({});
@ -630,9 +643,10 @@ class FakePipeRegistry extends PipeRegistry {
this.numberOfLookups = 0; this.numberOfLookups = 0;
} }
get(type:string, obj) { get(type:string, obj, bpc) {
if (type != this.pipeType) return null; if (type != this.pipeType) return null;
this.numberOfLookups ++; this.numberOfLookups ++;
this.bpc = bpc;
return this.factory(); return this.factory();
} }
} }

View File

@ -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", () => { it("should throw when no matching type", () => {
var r = new PipeRegistry({}); 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'` `Cannot find a pipe for type 'unknown' object 'some object'`
); );
}); });
@ -31,7 +31,7 @@ export function main() {
"type" : [] "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'` `Cannot find a pipe for type 'type' object 'some object'`
); );
}); });
@ -51,7 +51,7 @@ class PipeFactory {
return this.shouldSupport; return this.shouldSupport;
} }
create():Pipe { create(bpc):Pipe {
return this.pipe; return this.pipe;
} }
} }

View File

@ -12,7 +12,7 @@ import {ViewContainer} from 'angular2/src/core/compiler/view_container';
import {NgElement} from 'angular2/src/core/dom/element'; import {NgElement} from 'angular2/src/core/dom/element';
import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom'; import {LightDom, DestinationLightDom} from 'angular2/src/core/compiler/shadow_dom_emulation/light_dom';
import {Directive} from 'angular2/src/core/annotations/annotations'; 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 @proxy
@IMPLEMENTS(View) @IMPLEMENTS(View)

View File

@ -18,7 +18,7 @@ import {PromiseWrapper} from 'angular2/src/facade/async';
import {Injector, bind} from 'angular2/di'; import {Injector, bind} from 'angular2/di';
import {Lexer, Parser, dynamicChangeDetection, 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 {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader'; 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 {PrivateComponentLoader} from 'angular2/src/core/compiler/private_component_loader';
import {TemplateLoader} from 'angular2/src/core/compiler/template_loader'; import {TemplateLoader} from 'angular2/src/core/compiler/template_loader';
import {MockTemplateResolver} from 'angular2/src/mock/template_resolver_mock'; 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 {ComponentUrlMapper} from 'angular2/src/core/compiler/component_url_mapper';
import {UrlResolver} from 'angular2/src/core/compiler/url_resolver'; import {UrlResolver} from 'angular2/src/core/compiler/url_resolver';
import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver'; import {StyleUrlResolver} from 'angular2/src/core/compiler/style_url_resolver';
@ -869,7 +868,7 @@ class DoublePipeFactory {
return true; return true;
} }
create() { create(bpc) {
return new DoublePipe(); return new DoublePipe();
} }
} }

View File

@ -660,7 +660,7 @@ class DoublePipeFactory {
return true; return true;
} }
create() { create(bpc) {
return new DoublePipe(); return new DoublePipe();
} }
} }