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';
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';

View File

@ -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';

View File

@ -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;
}

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

View File

@ -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<String> {
getNonNullPipeNames():List<String> {
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 {

View File

@ -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;
}

View File

@ -168,11 +168,13 @@ export class Pipe extends AST {
exp:AST;
name:string;
args:List<AST>;
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) {

View File

@ -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;
}

View File

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

View File

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

View File

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

View File

@ -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);
}
}

View File

@ -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) {

View File

@ -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;

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 {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';

View File

@ -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';

View File

@ -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();
}
}

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", () => {
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;
}
}

View File

@ -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)

View File

@ -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();
}
}

View File

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