feat(change_detection): add support for pipes
This commit is contained in:
parent
fa25965939
commit
695b4ebbc7
|
@ -3,7 +3,6 @@ export {Lexer} from './src/change_detection/parser/lexer';
|
||||||
export {Parser} from './src/change_detection/parser/parser';
|
export {Parser} from './src/change_detection/parser/parser';
|
||||||
export {ContextWithVariableBindings}
|
export {ContextWithVariableBindings}
|
||||||
from './src/change_detection/parser/context_with_variable_bindings';
|
from './src/change_detection/parser/context_with_variable_bindings';
|
||||||
|
|
||||||
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
|
export {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError}
|
||||||
from './src/change_detection/exceptions';
|
from './src/change_detection/exceptions';
|
||||||
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
|
export {ChangeRecord, ChangeDispatcher, ChangeDetector,
|
||||||
|
@ -12,9 +11,15 @@ 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 * from './src/change_detection/pipes/pipe_registry';
|
||||||
|
export * from './src/change_detection/pipes/pipe';
|
||||||
|
|
||||||
|
|
||||||
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
|
import {ProtoChangeDetector, DynamicProtoChangeDetector, JitProtoChangeDetector}
|
||||||
from './src/change_detection/proto_change_detector';
|
from './src/change_detection/proto_change_detector';
|
||||||
|
import {PipeRegistry} from './src/change_detection/pipes/pipe_registry';
|
||||||
|
import {ArrayChanges} from './src/change_detection/pipes/array_changes';
|
||||||
|
import {NullPipe} from './src/change_detection/pipes/null_pipe';
|
||||||
|
|
||||||
export class ChangeDetection {
|
export class ChangeDetection {
|
||||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||||
|
@ -23,15 +28,30 @@ export class ChangeDetection {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export var defaultPipes = {
|
||||||
|
"[]" : [
|
||||||
|
{
|
||||||
|
"supports" : ArrayChanges.supportsObj,
|
||||||
|
"pipe" : () => new ArrayChanges()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"supports" : NullPipe.supportsObj,
|
||||||
|
"pipe" : () => new NullPipe()
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
|
||||||
|
var _registry = new PipeRegistry(defaultPipes);
|
||||||
|
|
||||||
export class DynamicChangeDetection extends ChangeDetection {
|
export class DynamicChangeDetection extends ChangeDetection {
|
||||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||||
return new DynamicProtoChangeDetector();
|
return new DynamicProtoChangeDetector(_registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class JitChangeDetection extends ChangeDetection {
|
export class JitChangeDetection extends ChangeDetection {
|
||||||
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
createProtoChangeDetector(name:string):ProtoChangeDetector{
|
||||||
return new JitProtoChangeDetector();
|
return new JitProtoChangeDetector(_registry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -95,6 +95,7 @@ var PROTOS_ACCESSOR = "this.protos";
|
||||||
var CHANGE_LOCAL = "change";
|
var CHANGE_LOCAL = "change";
|
||||||
var CHANGES_LOCAL = "changes";
|
var CHANGES_LOCAL = "changes";
|
||||||
var TEMP_LOCAL = "temp";
|
var TEMP_LOCAL = "temp";
|
||||||
|
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||||
|
|
||||||
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
||||||
return `
|
return `
|
||||||
|
@ -102,18 +103,19 @@ ${cons}
|
||||||
${detectChanges}
|
${detectChanges}
|
||||||
${setContext};
|
${setContext};
|
||||||
|
|
||||||
return function(dispatcher, formatters) {
|
return function(dispatcher, formatters, pipeRegistry) {
|
||||||
return new ${type}(dispatcher, formatters, protos);
|
return new ${type}(dispatcher, formatters, pipeRegistry, protos);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
||||||
return `
|
return `
|
||||||
var ${type} = function ${type}(dispatcher, formatters, protos) {
|
var ${type} = function ${type}(dispatcher, formatters, pipeRegistry, protos) {
|
||||||
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
||||||
${DISPATCHER_ACCESSOR} = dispatcher;
|
${DISPATCHER_ACCESSOR} = dispatcher;
|
||||||
${FORMATTERS_ACCESSOR} = formatters;
|
${FORMATTERS_ACCESSOR} = formatters;
|
||||||
|
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
||||||
${PROTOS_ACCESSOR} = protos;
|
${PROTOS_ACCESSOR} = protos;
|
||||||
${fieldsDefinitions}
|
${fieldsDefinitions}
|
||||||
}
|
}
|
||||||
|
@ -162,14 +164,18 @@ if (${CHANGES_LOCAL} && ${CHANGES_LOCAL}.length > 0) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function pipeCheckTemplate(context:string, pipe:string,
|
||||||
function structuralCheckTemplate(selfIndex:number, field:string, context:string, notify:string):string{
|
value:string, change:string, addRecord:string, notify:string):string{
|
||||||
return `
|
return `
|
||||||
${CHANGE_LOCAL} = ${UTIL}.structuralCheck(${field}, ${context});
|
if (${pipe} === ${UTIL}.unitialized() || !${pipe}.supports(${context})) {
|
||||||
if (${CHANGE_LOCAL}) {
|
${pipe} = ${PIPE_REGISTRY_ACCESSOR}.get('[]', ${context});
|
||||||
${CHANGES_LOCAL} = ${UTIL}.addRecord(${CHANGES_LOCAL},
|
}
|
||||||
${UTIL}.changeRecord(${PROTOS_ACCESSOR}[${selfIndex}].bindingMemento, ${CHANGE_LOCAL}));
|
|
||||||
${field} = ${CHANGE_LOCAL}.currentValue;
|
${CHANGE_LOCAL} = ${pipe}.transform(${context});
|
||||||
|
if (! ${UTIL}.noChangeMarker(${CHANGE_LOCAL})) {
|
||||||
|
${value} = ${CHANGE_LOCAL};
|
||||||
|
${change} = true;
|
||||||
|
${addRecord}
|
||||||
}
|
}
|
||||||
${notify}
|
${notify}
|
||||||
`;
|
`;
|
||||||
|
@ -235,6 +241,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
localNames:List<String>;
|
localNames:List<String>;
|
||||||
changeNames:List<String>;
|
changeNames:List<String>;
|
||||||
fieldNames:List<String>;
|
fieldNames:List<String>;
|
||||||
|
pipeNames:List<String>;
|
||||||
|
|
||||||
constructor(typeName:string, records:List<ProtoRecord>) {
|
constructor(typeName:string, records:List<ProtoRecord>) {
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
|
@ -243,6 +250,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
this.localNames = this.getLocalNames(records);
|
this.localNames = this.getLocalNames(records);
|
||||||
this.changeNames = this.getChangeNames(this.localNames);
|
this.changeNames = this.getChangeNames(this.localNames);
|
||||||
this.fieldNames = this.getFieldNames(this.localNames);
|
this.fieldNames = this.getFieldNames(this.localNames);
|
||||||
|
this.pipeNames = this.getPipeNames(this.localNames);
|
||||||
}
|
}
|
||||||
|
|
||||||
getLocalNames(records:List<ProtoRecord>):List<String> {
|
getLocalNames(records:List<ProtoRecord>):List<String> {
|
||||||
|
@ -262,6 +270,9 @@ export class ChangeDetectorJITGenerator {
|
||||||
return localNames.map((n) => `this.${n}`);
|
return localNames.map((n) => `this.${n}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPipeNames(localNames:List<String>):List<String> {
|
||||||
|
return localNames.map((n) => `this.${n}_pipe`);
|
||||||
|
}
|
||||||
|
|
||||||
generate():Function {
|
generate():Function {
|
||||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genSetContext());
|
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genSetContext());
|
||||||
|
@ -269,7 +280,16 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
genConstructor():string {
|
genConstructor():string {
|
||||||
return constructorTemplate(this.typeName, fieldDefinitionsTemplate(this.fieldNames));
|
var fields = [];
|
||||||
|
fields = fields.concat(this.fieldNames);
|
||||||
|
|
||||||
|
this.records.forEach((r) => {
|
||||||
|
if (r.mode === RECORD_TYPE_STRUCTURAL_CHECK) {
|
||||||
|
fields.push(this.pipeNames[r.selfIndex]);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return constructorTemplate(this.typeName, fieldDefinitionsTemplate(fields));
|
||||||
}
|
}
|
||||||
|
|
||||||
genSetContext():string {
|
genSetContext():string {
|
||||||
|
@ -295,17 +315,24 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
genRecord(r:ProtoRecord):string {
|
genRecord(r:ProtoRecord):string {
|
||||||
if (r.mode == RECORD_TYPE_STRUCTURAL_CHECK) {
|
if (r.mode === RECORD_TYPE_STRUCTURAL_CHECK) {
|
||||||
return this.getStructuralCheck(r);
|
return this.genPipeCheck (r);
|
||||||
} else {
|
} else {
|
||||||
return this.genReferenceCheck(r);
|
return this.genReferenceCheck(r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
getStructuralCheck(r:ProtoRecord):string {
|
genPipeCheck(r:ProtoRecord):string {
|
||||||
var field = this.fieldNames[r.selfIndex];
|
|
||||||
var context = this.localNames[r.contextIndex];
|
var context = this.localNames[r.contextIndex];
|
||||||
return structuralCheckTemplate(r.selfIndex - 1, field, context, this.genNotify(r));
|
var pipe = this.pipeNames[r.selfIndex];
|
||||||
|
var newValue = this.localNames[r.selfIndex];
|
||||||
|
var oldValue = this.fieldNames[r.selfIndex];
|
||||||
|
var change = this.changeNames[r.selfIndex];
|
||||||
|
|
||||||
|
var addRecord = addSimpleChangeRecordTemplate(r.selfIndex - 1, oldValue, newValue);
|
||||||
|
var notify = this.genNotify(r);
|
||||||
|
|
||||||
|
return pipeCheckTemplate(context, pipe, newValue, change, addRecord, notify);
|
||||||
}
|
}
|
||||||
|
|
||||||
genReferenceCheck(r:ProtoRecord):string {
|
genReferenceCheck(r:ProtoRecord):string {
|
||||||
|
|
|
@ -1,10 +1,9 @@
|
||||||
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
|
||||||
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
||||||
import {ArrayChanges} from './array_changes';
|
|
||||||
import {KeyValueChanges} from './keyvalue_changes';
|
|
||||||
import {ProtoRecord} from './proto_change_detector';
|
import {ProtoRecord} from './proto_change_detector';
|
||||||
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
import {ExpressionChangedAfterItHasBeenChecked} from './exceptions';
|
||||||
|
import {NO_CHANGE} from './pipes/pipe';
|
||||||
import {ChangeRecord, ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
|
import {ChangeRecord, ChangeDetector, CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './interfaces';
|
||||||
|
|
||||||
export var uninitialized = new Object();
|
export var uninitialized = new Object();
|
||||||
|
@ -85,10 +84,6 @@ function _changeRecord(bindingMemento, change) {
|
||||||
|
|
||||||
var _singleElementList = [null];
|
var _singleElementList = [null];
|
||||||
|
|
||||||
function _isBlank(val):boolean {
|
|
||||||
return isBlank(val) || val === uninitialized;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class ChangeDetectionUtil {
|
export class ChangeDetectionUtil {
|
||||||
static unitialized() {
|
static unitialized() {
|
||||||
return uninitialized;
|
return uninitialized;
|
||||||
|
@ -149,32 +144,6 @@ export class ChangeDetectionUtil {
|
||||||
return obj[args[0]];
|
return obj[args[0]];
|
||||||
}
|
}
|
||||||
|
|
||||||
static structuralCheck(self, context) {
|
|
||||||
if (_isBlank(self) && _isBlank(context)) {
|
|
||||||
return null;
|
|
||||||
} else if (_isBlank(context)) {
|
|
||||||
return new SimpleChange(null, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_isBlank(self)) {
|
|
||||||
if (ArrayChanges.supports(context)) {
|
|
||||||
self = new ArrayChanges();
|
|
||||||
} else if (KeyValueChanges.supports(context)) {
|
|
||||||
self = new KeyValueChanges();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isBlank(self) || !self.supportsObj(context)) {
|
|
||||||
throw new BaseException(`Unsupported type (${context})`);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (self.check(context)) {
|
|
||||||
return new SimpleChange(null, self); // TODO: don't wrap and return self instead
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static findContext(name:string, c){
|
static findContext(name:string, c){
|
||||||
while (c instanceof ContextWithVariableBindings) {
|
while (c instanceof ContextWithVariableBindings) {
|
||||||
if (c.hasBinding(name)) {
|
if (c.hasBinding(name)) {
|
||||||
|
@ -185,6 +154,10 @@ export class ChangeDetectionUtil {
|
||||||
return c;
|
return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static noChangeMarker(value):boolean {
|
||||||
|
return value === NO_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
static throwOnChange(proto:ProtoRecord, change) {
|
static throwOnChange(proto:ProtoRecord, change) {
|
||||||
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
|
throw new ExpressionChangedAfterItHasBeenChecked(proto, change);
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
||||||
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
import {ContextWithVariableBindings} from './parser/context_with_variable_bindings';
|
||||||
|
|
||||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||||
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
|
import {ChangeDetectionUtil, SimpleChange, uninitialized} from './change_detection_util';
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,16 +27,24 @@ import {ExpressionChangedAfterItHasBeenChecked, ChangeDetectionError} from './ex
|
||||||
export class DynamicChangeDetector extends AbstractChangeDetector {
|
export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
dispatcher:any;
|
dispatcher:any;
|
||||||
formatters:Map;
|
formatters:Map;
|
||||||
|
pipeRegistry;
|
||||||
|
|
||||||
values:List;
|
values:List;
|
||||||
changes:List;
|
changes:List;
|
||||||
|
pipes:List;
|
||||||
|
prevContexts:List;
|
||||||
|
|
||||||
protos:List<ProtoRecord>;
|
protos:List<ProtoRecord>;
|
||||||
|
|
||||||
constructor(dispatcher:any, formatters:Map, protoRecords:List<ProtoRecord>) {
|
constructor(dispatcher:any, formatters:Map, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
||||||
super();
|
super();
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.formatters = formatters;
|
this.formatters = formatters;
|
||||||
|
this.pipeRegistry = pipeRegistry;
|
||||||
|
|
||||||
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
this.values = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
|
this.pipes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
|
this.prevContexts = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
this.changes = ListWrapper.createFixedSize(protoRecords.length + 1);
|
||||||
|
|
||||||
this.protos = protoRecords;
|
this.protos = protoRecords;
|
||||||
|
@ -43,6 +52,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
|
|
||||||
setContext(context:any) {
|
setContext(context:any) {
|
||||||
ListWrapper.fill(this.values, uninitialized);
|
ListWrapper.fill(this.values, uninitialized);
|
||||||
|
ListWrapper.fill(this.changes, false);
|
||||||
|
ListWrapper.fill(this.pipes, null);
|
||||||
|
ListWrapper.fill(this.prevContexts, uninitialized);
|
||||||
this.values[0] = context;
|
this.values[0] = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -71,7 +83,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
_check(proto:ProtoRecord) {
|
_check(proto:ProtoRecord) {
|
||||||
try {
|
try {
|
||||||
if (proto.mode == RECORD_TYPE_STRUCTURAL_CHECK) {
|
if (proto.mode == RECORD_TYPE_STRUCTURAL_CHECK) {
|
||||||
return this._structuralCheck(proto);
|
return this._pipeCheck(proto);
|
||||||
} else {
|
} else {
|
||||||
return this._referenceCheck(proto);
|
return this._referenceCheck(proto);
|
||||||
}
|
}
|
||||||
|
@ -147,15 +159,36 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_structuralCheck(proto:ProtoRecord) {
|
_pipeCheck(proto:ProtoRecord) {
|
||||||
var self = this._readSelf(proto);
|
|
||||||
var context = this._readContext(proto);
|
var context = this._readContext(proto);
|
||||||
|
var pipe = this._pipeFor(proto, context);
|
||||||
|
|
||||||
var change = ChangeDetectionUtil.structuralCheck(self, context);
|
var newValue = pipe.transform(context);
|
||||||
if (isPresent(change)) {
|
if (! ChangeDetectionUtil.noChangeMarker(newValue)) {
|
||||||
this._writeSelf(proto, change.currentValue);
|
this._writeSelf(proto, newValue);
|
||||||
|
this._setChanged(proto, true);
|
||||||
|
|
||||||
|
if (proto.lastInBinding) {
|
||||||
|
var prevValue = this._readSelf(proto);
|
||||||
|
return ChangeDetectionUtil.simpleChange(prevValue, newValue);
|
||||||
|
} else {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._setChanged(proto, false);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_pipeFor(proto:ProtoRecord, context) {
|
||||||
|
var storedPipe = this._readPipe(proto);
|
||||||
|
if (isPresent(storedPipe) && storedPipe.supports(context)) {
|
||||||
|
return storedPipe;
|
||||||
|
} else {
|
||||||
|
var pipe = this.pipeRegistry.get("[]", context);
|
||||||
|
this._writePipe(proto, pipe);
|
||||||
|
return pipe;
|
||||||
}
|
}
|
||||||
return change;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_readContext(proto:ProtoRecord) {
|
_readContext(proto:ProtoRecord) {
|
||||||
|
@ -170,6 +203,14 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
this.values[proto.selfIndex] = value;
|
this.values[proto.selfIndex] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_readPipe(proto:ProtoRecord) {
|
||||||
|
return this.pipes[proto.selfIndex];
|
||||||
|
}
|
||||||
|
|
||||||
|
_writePipe(proto:ProtoRecord, value) {
|
||||||
|
this.pipes[proto.selfIndex] = value;
|
||||||
|
}
|
||||||
|
|
||||||
_setChanged(proto:ProtoRecord, value:boolean) {
|
_setChanged(proto:ProtoRecord, value:boolean) {
|
||||||
this.changes[proto.selfIndex] = value;
|
this.changes[proto.selfIndex] = value;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,9 @@ import {
|
||||||
looseIdentical,
|
looseIdentical,
|
||||||
} from 'angular2/src/facade/lang';
|
} from 'angular2/src/facade/lang';
|
||||||
|
|
||||||
export class ArrayChanges {
|
import {NO_CHANGE, Pipe} from './pipe';
|
||||||
|
|
||||||
|
export class ArrayChanges extends Pipe {
|
||||||
_collection;
|
_collection;
|
||||||
_length:int;
|
_length:int;
|
||||||
_linkedRecords:_DuplicateMap;
|
_linkedRecords:_DuplicateMap;
|
||||||
|
@ -30,6 +32,7 @@ export class ArrayChanges {
|
||||||
_removalsTail:CollectionChangeRecord;
|
_removalsTail:CollectionChangeRecord;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
|
super();
|
||||||
this._collection = null;
|
this._collection = null;
|
||||||
this._length = null;
|
this._length = null;
|
||||||
/// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
/// Keeps track of the used records at any point in time (during & across `_check()` calls)
|
||||||
|
@ -48,12 +51,12 @@ export class ArrayChanges {
|
||||||
this._removalsTail = null;
|
this._removalsTail = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
static supports(obj):boolean {
|
static supportsObj(obj):boolean {
|
||||||
return isListLikeIterable(obj);
|
return isListLikeIterable(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
supportsObj(obj):boolean {
|
supports(obj):boolean {
|
||||||
return ArrayChanges.supports(obj);
|
return ArrayChanges.supportsObj(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
get collection() {
|
get collection() {
|
||||||
|
@ -99,6 +102,14 @@ export class ArrayChanges {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
transform(collection){
|
||||||
|
if (this.check(collection)) {
|
||||||
|
return this;
|
||||||
|
} else {
|
||||||
|
return NO_CHANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
// todo(vicb): optim for UnmodifiableListView (frozen arrays)
|
||||||
check(collection):boolean {
|
check(collection):boolean {
|
||||||
this._reset();
|
this._reset();
|
|
@ -0,0 +1,27 @@
|
||||||
|
import {isBlank} from 'angular2/src/facade/lang';
|
||||||
|
import {Pipe, NO_CHANGE} from './pipe';
|
||||||
|
|
||||||
|
export class NullPipe extends Pipe {
|
||||||
|
called:boolean;
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
this.called = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static supportsObj(obj):boolean {
|
||||||
|
return isBlank(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(obj) {
|
||||||
|
return NullPipe.supportsObj(obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(value) {
|
||||||
|
if (! this.called) {
|
||||||
|
this.called = true;
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return NO_CHANGE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
export var NO_CHANGE = new Object();
|
||||||
|
|
||||||
|
export class Pipe {
|
||||||
|
supports(obj):boolean {return false;}
|
||||||
|
transform(value:any):any {return null;}
|
||||||
|
}
|
|
@ -0,0 +1,27 @@
|
||||||
|
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
import {isBlank, isPresent, BaseException, CONST} from 'angular2/src/facade/lang';
|
||||||
|
import {Pipe} from './pipe';
|
||||||
|
|
||||||
|
export class PipeRegistry {
|
||||||
|
config;
|
||||||
|
|
||||||
|
constructor(config){
|
||||||
|
this.config = config;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(type:string, obj):Pipe {
|
||||||
|
var listOfConfigs = this.config[type];
|
||||||
|
if (isBlank(listOfConfigs)) {
|
||||||
|
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
var matchingConfig = ListWrapper.find(listOfConfigs,
|
||||||
|
(pipeConfig) => pipeConfig["supports"](obj));
|
||||||
|
|
||||||
|
if (isBlank(matchingConfig)) {
|
||||||
|
throw new BaseException(`Cannot find a pipe for type '${type}' object '${obj}'`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return matchingConfig["pipe"]();
|
||||||
|
}
|
||||||
|
}
|
|
@ -27,6 +27,7 @@ import {ChangeRecord, ChangeDispatcher, ChangeDetector} from './interfaces';
|
||||||
import {ChangeDetectionUtil} from './change_detection_util';
|
import {ChangeDetectionUtil} from './change_detection_util';
|
||||||
import {DynamicChangeDetector} from './dynamic_change_detector';
|
import {DynamicChangeDetector} from './dynamic_change_detector';
|
||||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||||
|
import {PipeRegistry} from './pipes/pipe_registry';
|
||||||
|
|
||||||
import {coalesce} from './coalesce';
|
import {coalesce} from './coalesce';
|
||||||
|
|
||||||
|
@ -89,6 +90,7 @@ export class ProtoRecord {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export class ProtoChangeDetector {
|
export class ProtoChangeDetector {
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, structural:boolean = false){}
|
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null, structural:boolean = false){}
|
||||||
instantiate(dispatcher:any, formatters:Map):ChangeDetector{
|
instantiate(dispatcher:any, formatters:Map):ChangeDetector{
|
||||||
|
@ -99,9 +101,11 @@ export class ProtoChangeDetector {
|
||||||
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||||
_records:List<ProtoRecord>;
|
_records:List<ProtoRecord>;
|
||||||
_recordBuilder:ProtoRecordBuilder;
|
_recordBuilder:ProtoRecordBuilder;
|
||||||
|
_pipeRegistry:PipeRegistry;
|
||||||
|
|
||||||
constructor() {
|
constructor(pipeRegistry:PipeRegistry) {
|
||||||
super();
|
super();
|
||||||
|
this._pipeRegistry = pipeRegistry;
|
||||||
this._records = null;
|
this._records = null;
|
||||||
this._recordBuilder = new ProtoRecordBuilder();
|
this._recordBuilder = new ProtoRecordBuilder();
|
||||||
}
|
}
|
||||||
|
@ -112,7 +116,8 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||||
|
|
||||||
instantiate(dispatcher:any, formatters:Map) {
|
instantiate(dispatcher:any, formatters:Map) {
|
||||||
this._createRecordsIfNecessary();
|
this._createRecordsIfNecessary();
|
||||||
return new DynamicChangeDetector(dispatcher, formatters, this._records);
|
return new DynamicChangeDetector(dispatcher, formatters,
|
||||||
|
this._pipeRegistry, this._records);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createRecordsIfNecessary() {
|
_createRecordsIfNecessary() {
|
||||||
|
@ -127,9 +132,11 @@ var _jitProtoChangeDetectorClassCounter:number = 0;
|
||||||
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
_factory:Function;
|
_factory:Function;
|
||||||
_recordBuilder:ProtoRecordBuilder;
|
_recordBuilder:ProtoRecordBuilder;
|
||||||
|
_pipeRegistry;
|
||||||
|
|
||||||
constructor() {
|
constructor(pipeRegistry) {
|
||||||
super();
|
super();
|
||||||
|
this._pipeRegistry = pipeRegistry;
|
||||||
this._factory = null;
|
this._factory = null;
|
||||||
this._recordBuilder = new ProtoRecordBuilder();
|
this._recordBuilder = new ProtoRecordBuilder();
|
||||||
}
|
}
|
||||||
|
@ -140,7 +147,7 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
|
|
||||||
instantiate(dispatcher:any, formatters:Map) {
|
instantiate(dispatcher:any, formatters:Map) {
|
||||||
this._createFactoryIfNecessary();
|
this._createFactoryIfNecessary();
|
||||||
return this._factory(dispatcher, formatters);
|
return this._factory(dispatcher, formatters, this._pipeRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createFactoryIfNecessary() {
|
_createFactoryIfNecessary() {
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
import {Viewport, onChange} from 'angular2/src/core/annotations/annotations';
|
import {Viewport} from 'angular2/src/core/annotations/annotations';
|
||||||
import {OnChange} from 'angular2/src/core/compiler/interfaces';
|
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {View} from 'angular2/src/core/compiler/view';
|
import {View} from 'angular2/src/core/compiler/view';
|
||||||
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
import {isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||||
|
@ -7,21 +6,19 @@ import {ListWrapper} from 'angular2/src/facade/collection';
|
||||||
|
|
||||||
@Viewport({
|
@Viewport({
|
||||||
selector: '[foreach][in]',
|
selector: '[foreach][in]',
|
||||||
lifecycle: [onChange],
|
|
||||||
bind: {
|
bind: {
|
||||||
'in': 'iterable[]'
|
'in': 'iterableChanges[]'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
export class Foreach extends OnChange {
|
export class Foreach {
|
||||||
viewContainer: ViewContainer;
|
viewContainer: ViewContainer;
|
||||||
iterable;
|
constructor(viewContainer:ViewContainer) {
|
||||||
constructor(viewContainer: ViewContainer) {
|
|
||||||
super();
|
super();
|
||||||
this.viewContainer = viewContainer;
|
this.viewContainer = viewContainer;
|
||||||
}
|
}
|
||||||
onChange(changes) {
|
|
||||||
var iteratorChanges = changes['iterable'];
|
set iterableChanges(changes) {
|
||||||
if (isBlank(iteratorChanges) || isBlank(iteratorChanges.currentValue)) {
|
if (isBlank(changes)) {
|
||||||
this.viewContainer.clear();
|
this.viewContainer.clear();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -29,17 +26,17 @@ export class Foreach extends OnChange {
|
||||||
// TODO(rado): check if change detection can produce a change record that is
|
// TODO(rado): check if change detection can produce a change record that is
|
||||||
// easier to consume than current.
|
// easier to consume than current.
|
||||||
var recordViewTuples = [];
|
var recordViewTuples = [];
|
||||||
iteratorChanges.currentValue.forEachRemovedItem(
|
changes.forEachRemovedItem(
|
||||||
(removedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(removedRecord, null))
|
(removedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(removedRecord, null))
|
||||||
);
|
);
|
||||||
|
|
||||||
iteratorChanges.currentValue.forEachMovedItem(
|
changes.forEachMovedItem(
|
||||||
(movedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(movedRecord, null))
|
(movedRecord) => ListWrapper.push(recordViewTuples, new RecordViewTuple(movedRecord, null))
|
||||||
);
|
);
|
||||||
|
|
||||||
var insertTuples = Foreach.bulkRemove(recordViewTuples, this.viewContainer);
|
var insertTuples = Foreach.bulkRemove(recordViewTuples, this.viewContainer);
|
||||||
|
|
||||||
iteratorChanges.currentValue.forEachAddedItem(
|
changes.forEachAddedItem(
|
||||||
(addedRecord) => ListWrapper.push(insertTuples, new RecordViewTuple(addedRecord, null))
|
(addedRecord) => ListWrapper.push(insertTuples, new RecordViewTuple(addedRecord, null))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||||
import {ArrayChanges} from 'angular2/src/change_detection/array_changes';
|
import {ArrayChanges} from 'angular2/src/change_detection/pipes/array_changes';
|
||||||
|
|
||||||
import {NumberWrapper} from 'angular2/src/facade/lang';
|
import {NumberWrapper} from 'angular2/src/facade/lang';
|
||||||
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
import {ListWrapper, MapWrapper} from 'angular2/src/facade/collection';
|
||||||
|
@ -23,10 +23,10 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support list and iterables', () => {
|
it('should support list and iterables', () => {
|
||||||
expect(ArrayChanges.supports([])).toBeTruthy();
|
expect(ArrayChanges.supportsObj([])).toBeTruthy();
|
||||||
expect(ArrayChanges.supports(new TestIterable())).toBeTruthy();
|
expect(ArrayChanges.supportsObj(new TestIterable())).toBeTruthy();
|
||||||
expect(ArrayChanges.supports(MapWrapper.create())).toBeFalsy();
|
expect(ArrayChanges.supportsObj(MapWrapper.create())).toBeFalsy();
|
||||||
expect(ArrayChanges.supports(null)).toBeFalsy();
|
expect(ArrayChanges.supportsObj(null)).toBeFalsy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support iterables', () => {
|
it('should support iterables', () => {
|
||||||
|
|
|
@ -8,6 +8,7 @@ import {Lexer} from 'angular2/src/change_detection/parser/lexer';
|
||||||
import {arrayChangesAsString, kvChangesAsString} from './util';
|
import {arrayChangesAsString, kvChangesAsString} from './util';
|
||||||
|
|
||||||
import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, ContextWithVariableBindings,
|
import {ChangeDispatcher, DynamicChangeDetector, ChangeDetectionError, ContextWithVariableBindings,
|
||||||
|
PipeRegistry, NO_CHANGE,
|
||||||
CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from 'angular2/change_detection';
|
CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from 'angular2/change_detection';
|
||||||
|
|
||||||
|
|
||||||
|
@ -17,8 +18,8 @@ import {JitProtoChangeDetector, DynamicProtoChangeDetector} from 'angular2/src/c
|
||||||
export function main() {
|
export function main() {
|
||||||
describe("change detection", () => {
|
describe("change detection", () => {
|
||||||
StringMapWrapper.forEach(
|
StringMapWrapper.forEach(
|
||||||
{ "dynamic": () => new DynamicProtoChangeDetector(),
|
{ "dynamic": (registry = null) => new DynamicProtoChangeDetector(registry),
|
||||||
"JIT": () => new JitProtoChangeDetector()
|
"JIT": (registry = null) => new JitProtoChangeDetector(registry)
|
||||||
}, (createProtoChangeDetector, name) => {
|
}, (createProtoChangeDetector, name) => {
|
||||||
|
|
||||||
if (name == "JIT" && IS_DARTIUM) return;
|
if (name == "JIT" && IS_DARTIUM) return;
|
||||||
|
@ -29,8 +30,8 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function createChangeDetector(memo:string, exp:string, context = null, formatters = null,
|
function createChangeDetector(memo:string, exp:string, context = null, formatters = null,
|
||||||
structural = false) {
|
registry = null, structural = false) {
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector(registry);
|
||||||
pcd.addAst(ast(exp), memo, memo, structural);
|
pcd.addAst(ast(exp), memo, memo, structural);
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
|
@ -41,8 +42,8 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function executeWatch(memo:string, exp:string, context = null, formatters = null,
|
function executeWatch(memo:string, exp:string, context = null, formatters = null,
|
||||||
content = false) {
|
registry = null, content = false) {
|
||||||
var res = createChangeDetector(memo, exp, context, formatters, content);
|
var res = createChangeDetector(memo, exp, context, formatters, registry, content);
|
||||||
res["changeDetector"].detectChanges();
|
res["changeDetector"].detectChanges();
|
||||||
return res["dispatcher"].log;
|
return res["dispatcher"].log;
|
||||||
}
|
}
|
||||||
|
@ -281,124 +282,6 @@ export function main() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("collections", () => {
|
|
||||||
it("should not register a change when going from null to null", () => {
|
|
||||||
var context = new TestData(null);
|
|
||||||
|
|
||||||
var c = createChangeDetector('a', 'a', context, null, true);
|
|
||||||
var cd = c["changeDetector"];
|
|
||||||
var dispatcher = c["dispatcher"];
|
|
||||||
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual([]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should register changes when switching from null to collection and back", () => {
|
|
||||||
var context = new TestData(null);
|
|
||||||
|
|
||||||
var c = createChangeDetector('a', 'a', context, null, true);
|
|
||||||
var cd = c["changeDetector"];
|
|
||||||
var dispatcher = c["dispatcher"];
|
|
||||||
|
|
||||||
context.a = [0];
|
|
||||||
cd.detectChanges();
|
|
||||||
|
|
||||||
expect(dispatcher.log).toEqual(["a=" +
|
|
||||||
arrayChangesAsString({
|
|
||||||
collection: ['0[null->0]'],
|
|
||||||
additions: ['0[null->0]']
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
dispatcher.clear();
|
|
||||||
|
|
||||||
context.a = null;
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual(['a=null']);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("list", () => {
|
|
||||||
it("should support list changes", () => {
|
|
||||||
var context = new TestData([1, 2]);
|
|
||||||
|
|
||||||
expect(executeWatch("a", "a", context, null, true))
|
|
||||||
.toEqual(["a=" +
|
|
||||||
arrayChangesAsString({
|
|
||||||
collection: ['1[null->0]', '2[null->1]'],
|
|
||||||
additions: ['1[null->0]', '2[null->1]']
|
|
||||||
})]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle reference changes", () => {
|
|
||||||
var context = new TestData([1, 2]);
|
|
||||||
var objs = createChangeDetector("a", "a", context, null, true);
|
|
||||||
var cd = objs["changeDetector"];
|
|
||||||
var dispatcher = objs["dispatcher"];
|
|
||||||
cd.detectChanges();
|
|
||||||
dispatcher.clear();
|
|
||||||
|
|
||||||
context.a = [2, 1];
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual(["a=" +
|
|
||||||
arrayChangesAsString({
|
|
||||||
collection: ['2[1->0]', '1[0->1]'],
|
|
||||||
previous: ['1[0->1]', '2[1->0]'],
|
|
||||||
moves: ['2[1->0]', '1[0->1]']
|
|
||||||
})]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("map", () => {
|
|
||||||
it("should support map changes", () => {
|
|
||||||
var map = MapWrapper.create();
|
|
||||||
MapWrapper.set(map, "foo", "bar");
|
|
||||||
var context = new TestData(map);
|
|
||||||
expect(executeWatch("a", "a", context, null, true))
|
|
||||||
.toEqual(["a=" +
|
|
||||||
kvChangesAsString({
|
|
||||||
map: ['foo[null->bar]'],
|
|
||||||
additions: ['foo[null->bar]']
|
|
||||||
})]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle reference changes", () => {
|
|
||||||
var map = MapWrapper.create();
|
|
||||||
MapWrapper.set(map, "foo", "bar");
|
|
||||||
var context = new TestData(map);
|
|
||||||
var objs = createChangeDetector("a", "a", context, null, true);
|
|
||||||
var cd = objs["changeDetector"];
|
|
||||||
var dispatcher = objs["dispatcher"];
|
|
||||||
cd.detectChanges();
|
|
||||||
dispatcher.clear();
|
|
||||||
|
|
||||||
context.a = MapWrapper.create();
|
|
||||||
MapWrapper.set(context.a, "bar", "foo");
|
|
||||||
cd.detectChanges();
|
|
||||||
expect(dispatcher.log).toEqual(["a=" +
|
|
||||||
kvChangesAsString({
|
|
||||||
map: ['bar[null->foo]'],
|
|
||||||
previous: ['foo[bar->null]'],
|
|
||||||
additions: ['bar[null->foo]'],
|
|
||||||
removals: ['foo[bar->null]']
|
|
||||||
})]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!IS_DARTIUM) {
|
|
||||||
describe("js objects", () => {
|
|
||||||
it("should support object changes", () => {
|
|
||||||
var map = {"foo": "bar"};
|
|
||||||
var context = new TestData(map);
|
|
||||||
expect(executeWatch("a", "a", context, null, true))
|
|
||||||
.toEqual(["a=" +
|
|
||||||
kvChangesAsString({
|
|
||||||
map: ['foo[null->bar]'],
|
|
||||||
additions: ['foo[null->bar]']
|
|
||||||
})]);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
describe("ContextWithVariableBindings", () => {
|
describe("ContextWithVariableBindings", () => {
|
||||||
it('should read a field from ContextWithVariableBindings', () => {
|
it('should read a field from ContextWithVariableBindings', () => {
|
||||||
var locals = new ContextWithVariableBindings(null,
|
var locals = new ContextWithVariableBindings(null,
|
||||||
|
@ -543,8 +426,130 @@ export function main() {
|
||||||
expect(checkedChild.mode).toEqual(CHECK_ONCE);
|
expect(checkedChild.mode).toEqual(CHECK_ONCE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("pipes", () => {
|
||||||
|
it("should support pipes", () => {
|
||||||
|
var registry = new FakePipeRegistry(() => new CountingPipe());
|
||||||
|
var ctx = new Person("Megatron");
|
||||||
|
|
||||||
|
var c = createChangeDetector("memo", "name", ctx, null, registry, true);
|
||||||
|
var cd = c["changeDetector"];
|
||||||
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.log).toEqual(['memo=Megatron state:0']);
|
||||||
|
|
||||||
|
dispatcher.clear();
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.log).toEqual(['memo=Megatron state:1']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should lookup pipes in the registry when the context is not supported", () => {
|
||||||
|
var registry = new FakePipeRegistry(() => new OncePipe());
|
||||||
|
var ctx = new Person("Megatron");
|
||||||
|
|
||||||
|
var c = createChangeDetector("memo", "name", ctx, null, registry, true);
|
||||||
|
var cd = c["changeDetector"];
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(registry.numberOfLookups).toEqual(1);
|
||||||
|
|
||||||
|
ctx.name = "Optimus Prime";
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(registry.numberOfLookups).toEqual(2);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should do nothing when returns NO_CHANGE", () => {
|
||||||
|
var registry = new FakePipeRegistry(() => new IdentityPipe())
|
||||||
|
var ctx = new Person("Megatron");
|
||||||
|
|
||||||
|
var c = createChangeDetector("memo", "name", ctx, null, registry, true);
|
||||||
|
var cd = c["changeDetector"];
|
||||||
|
var dispatcher = c["dispatcher"];
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.log).toEqual(['memo=Megatron']);
|
||||||
|
|
||||||
|
ctx.name = "Optimus Prime";
|
||||||
|
dispatcher.clear();
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.log).toEqual(['memo=Optimus Prime']);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
class CountingPipe {
|
||||||
|
state:number;
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.state = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(newValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(value) {
|
||||||
|
return `${value} state:${this.state ++}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class OncePipe {
|
||||||
|
called:boolean;
|
||||||
|
constructor() {
|
||||||
|
this.called = false;;
|
||||||
|
}
|
||||||
|
|
||||||
|
supports(newValue) {
|
||||||
|
return !this.called;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(value) {
|
||||||
|
this.called = true;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class IdentityPipe {
|
||||||
|
state:any;
|
||||||
|
|
||||||
|
supports(newValue) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
transform(value) {
|
||||||
|
if (this.state === value) {
|
||||||
|
return NO_CHANGE;
|
||||||
|
} else {
|
||||||
|
this.state = value;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class FakePipeRegistry extends PipeRegistry {
|
||||||
|
numberOfLookups:number;
|
||||||
|
factory:Function;
|
||||||
|
|
||||||
|
constructor(factory) {
|
||||||
|
super({});
|
||||||
|
this.factory = factory;
|
||||||
|
this.numberOfLookups = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
get(type:string, obj) {
|
||||||
|
this.numberOfLookups ++;
|
||||||
|
return this.factory();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class TestRecord {
|
class TestRecord {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
import {describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||||
import {KeyValueChanges} from 'angular2/src/change_detection/keyvalue_changes';
|
import {KeyValueChanges} from 'angular2/src/change_detection/pipes/keyvalue_changes';
|
||||||
import {NumberWrapper, isJsObject} from 'angular2/src/facade/lang';
|
import {NumberWrapper, isJsObject} from 'angular2/src/facade/lang';
|
||||||
import {MapWrapper} from 'angular2/src/facade/collection';
|
import {MapWrapper} from 'angular2/src/facade/collection';
|
||||||
import {kvChangesAsString} from './util';
|
import {kvChangesAsString} from './util';
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
import {ddescribe, describe, it, iit, xit, expect, beforeEach, afterEach} from 'angular2/test_lib';
|
||||||
|
|
||||||
|
import {PipeRegistry} from 'angular2/src/change_detection/pipes/pipe_registry';
|
||||||
|
import {Pipe} from 'angular2/src/change_detection/pipes/pipe';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("pipe registry", () => {
|
||||||
|
var firstPipe = new Pipe();
|
||||||
|
var secondPipe = new Pipe();
|
||||||
|
|
||||||
|
it("should return the first pipe supporting the data type", () => {
|
||||||
|
var r = new PipeRegistry({
|
||||||
|
"type": [
|
||||||
|
{"supports": (obj) => false, "pipe": () => firstPipe},
|
||||||
|
{"supports": (obj) => true, "pipe": () => secondPipe}
|
||||||
|
]
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(r.get("type", "some object")).toBe(secondPipe);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw when no matching type", () => {
|
||||||
|
var r = new PipeRegistry({});
|
||||||
|
expect(() => r.get("unknown", "some object")).toThrowError(
|
||||||
|
`Cannot find a pipe for type 'unknown' object 'some object'`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should throw when no matching pipe", () => {
|
||||||
|
var r = new PipeRegistry({
|
||||||
|
"type" : []
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => r.get("type", "some object")).toThrowError(
|
||||||
|
`Cannot find a pipe for type 'type' object 'some object'`
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
|
@ -71,7 +71,7 @@ export function main() {
|
||||||
if (isPresent(current.element.getAttribute('viewroot'))) {
|
if (isPresent(current.element.getAttribute('viewroot'))) {
|
||||||
current.isViewRoot = true;
|
current.isViewRoot = true;
|
||||||
current.inheritedProtoView = new ProtoView(current.element,
|
current.inheritedProtoView = new ProtoView(current.element,
|
||||||
new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
} else if (isPresent(parent)) {
|
} else if (isPresent(parent)) {
|
||||||
current.inheritedProtoView = parent.inheritedProtoView;
|
current.inheritedProtoView = parent.inheritedProtoView;
|
||||||
}
|
}
|
||||||
|
@ -379,7 +379,7 @@ export function main() {
|
||||||
var results = pipeline.process(el('<div viewroot prop-binding directives></div>'));
|
var results = pipeline.process(el('<div viewroot prop-binding directives></div>'));
|
||||||
var pv = results[0].inheritedProtoView;
|
var pv = results[0].inheritedProtoView;
|
||||||
results[0].inheritedElementBinder.nestedProtoView = new ProtoView(
|
results[0].inheritedElementBinder.nestedProtoView = new ProtoView(
|
||||||
el('<div></div>'), new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
el('<div></div>'), new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
|
|
||||||
instantiateView(pv);
|
instantiateView(pv);
|
||||||
evalContext.prop1 = 'a';
|
evalContext.prop1 = 'a';
|
||||||
|
|
|
@ -15,9 +15,9 @@ export function main() {
|
||||||
function createPipeline(selector, strategy:ShadowDomStrategy, styleHost) {
|
function createPipeline(selector, strategy:ShadowDomStrategy, styleHost) {
|
||||||
var component = new Component({selector: selector});
|
var component = new Component({selector: selector});
|
||||||
var meta = new DirectiveMetadata(null, component);
|
var meta = new DirectiveMetadata(null, component);
|
||||||
var transformer = new ShadowDomTransformer(meta, strategy, styleHost);
|
var pipe = new ShadowDomTransformer(meta, strategy, styleHost);
|
||||||
transformer.clearCache();
|
pipe.clearCache();
|
||||||
return new CompilePipeline([transformer]);
|
return new CompilePipeline([pipe]);
|
||||||
}
|
}
|
||||||
|
|
||||||
it('it should set ignoreBindings to true for style elements', () => {
|
it('it should set ignoreBindings to true for style elements', () => {
|
||||||
|
|
|
@ -10,7 +10,7 @@ import {NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_str
|
||||||
import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular2/change_detection';
|
import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular2/change_detection';
|
||||||
|
|
||||||
function createView(nodes) {
|
function createView(nodes) {
|
||||||
var view = new View(null, nodes, new DynamicProtoChangeDetector(), MapWrapper.create());
|
var view = new View(null, nodes, new DynamicProtoChangeDetector(null), MapWrapper.create());
|
||||||
view.init([], [], [], [], [], [], []);
|
view.init([], [], [], [], [], [], []);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +69,7 @@ export function main() {
|
||||||
dom = el(`<div><stuff></stuff><div insert-after-me></div><stuff></stuff></div>`);
|
dom = el(`<div><stuff></stuff><div insert-after-me></div><stuff></stuff></div>`);
|
||||||
var insertionElement = dom.childNodes[1];
|
var insertionElement = dom.childNodes[1];
|
||||||
parentView = createView([dom.childNodes[0]]);
|
parentView = createView([dom.childNodes[0]]);
|
||||||
protoView = new ProtoView(el('<div>hi</div>'), new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
protoView = new ProtoView(el('<div>hi</div>'), new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
elementInjector = new ElementInjector(null, null, null, null);
|
elementInjector = new ElementInjector(null, null, null, null);
|
||||||
viewContainer = new ViewContainer(parentView, insertionElement, protoView, elementInjector, null);
|
viewContainer = new ViewContainer(parentView, insertionElement, protoView, elementInjector, null);
|
||||||
customViewWithOneNode = createView([el('<div>single</div>')]);
|
customViewWithOneNode = createView([el('<div>single</div>')]);
|
||||||
|
@ -213,7 +213,7 @@ export function main() {
|
||||||
viewContainer.hydrate(new Injector([]), null);
|
viewContainer.hydrate(new Injector([]), null);
|
||||||
|
|
||||||
var pv = new ProtoView(el('<div class="ng-binding">{{}}</div>'),
|
var pv = new ProtoView(el('<div class="ng-binding">{{}}</div>'),
|
||||||
new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
||||||
pv.bindTextNode(0, parser.parseBinding('foo', null));
|
pv.bindTextNode(0, parser.parseBinding('foo', null));
|
||||||
fancyView = pv.instantiate(null, null);
|
fancyView = pv.instantiate(null, null);
|
||||||
|
|
|
@ -59,7 +59,7 @@ export function main() {
|
||||||
describe('instantiated from protoView', () => {
|
describe('instantiated from protoView', () => {
|
||||||
var view;
|
var view;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
|
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(null), null);
|
||||||
view = pv.instantiate(null, null);
|
view = pv.instantiate(null, null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the view pool to reuse views', () => {
|
it('should use the view pool to reuse views', () => {
|
||||||
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
|
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(null), null);
|
||||||
var fakeView = new FakeView();
|
var fakeView = new FakeView();
|
||||||
pv.returnToPool(fakeView);
|
pv.returnToPool(fakeView);
|
||||||
|
|
||||||
|
@ -88,7 +88,7 @@ export function main() {
|
||||||
describe('with locals', function() {
|
describe('with locals', function() {
|
||||||
var view;
|
var view;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
|
var pv = new ProtoView(el('<div id="1"></div>'), new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindVariable('context-foo', 'template-foo');
|
pv.bindVariable('context-foo', 'template-foo');
|
||||||
view = createView(pv);
|
view = createView(pv);
|
||||||
});
|
});
|
||||||
|
@ -125,7 +125,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect the root node in the ProtoView element', () => {
|
it('should collect the root node in the ProtoView element', () => {
|
||||||
var pv = new ProtoView(templateAwareCreateElement('<div id="1"></div>'),
|
var pv = new ProtoView(templateAwareCreateElement('<div id="1"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
var view = pv.instantiate(null, null);
|
var view = pv.instantiate(null, null);
|
||||||
view.hydrate(null, null, null);
|
view.hydrate(null, null, null);
|
||||||
expect(view.nodes.length).toBe(1);
|
expect(view.nodes.length).toBe(1);
|
||||||
|
@ -136,7 +136,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect property bindings on the root element if it has the ng-binding class', () => {
|
it('should collect property bindings on the root element if it has the ng-binding class', () => {
|
||||||
var pv = new ProtoView(templateAwareCreateElement('<div [prop]="a" class="ng-binding"></div>'),
|
var pv = new ProtoView(templateAwareCreateElement('<div [prop]="a" class="ng-binding"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(null);
|
pv.bindElement(null);
|
||||||
pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop'));
|
pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop'));
|
||||||
|
|
||||||
|
@ -148,7 +148,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect property bindings on child elements with ng-binding class', () => {
|
it('should collect property bindings on child elements with ng-binding class', () => {
|
||||||
var pv = new ProtoView(templateAwareCreateElement('<div><span></span><span class="ng-binding"></span></div>'),
|
var pv = new ProtoView(templateAwareCreateElement('<div><span></span><span class="ng-binding"></span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(null);
|
pv.bindElement(null);
|
||||||
pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a'));
|
pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a'));
|
||||||
|
|
||||||
|
@ -164,7 +164,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect text nodes under the root element', () => {
|
it('should collect text nodes under the root element', () => {
|
||||||
var pv = new ProtoView(templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'),
|
var pv = new ProtoView(templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(null);
|
pv.bindElement(null);
|
||||||
pv.bindTextNode(0, parser.parseBinding('a', null));
|
pv.bindTextNode(0, parser.parseBinding('a', null));
|
||||||
pv.bindTextNode(2, parser.parseBinding('b', null));
|
pv.bindTextNode(2, parser.parseBinding('b', null));
|
||||||
|
@ -178,7 +178,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect text nodes with bindings on child elements with ng-binding class', () => {
|
it('should collect text nodes with bindings on child elements with ng-binding class', () => {
|
||||||
var pv = new ProtoView(templateAwareCreateElement('<div><span> </span><span class="ng-binding">{{}}</span></div>'),
|
var pv = new ProtoView(templateAwareCreateElement('<div><span> </span><span class="ng-binding">{{}}</span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(null);
|
pv.bindElement(null);
|
||||||
pv.bindTextNode(0, parser.parseBinding('b', null));
|
pv.bindTextNode(0, parser.parseBinding('b', null));
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ export function main() {
|
||||||
describe('inplace instantiation', () => {
|
describe('inplace instantiation', () => {
|
||||||
it('should be supported.', () => {
|
it('should be supported.', () => {
|
||||||
var template = el('<div></div>');
|
var template = el('<div></div>');
|
||||||
var pv = new ProtoView(template, new DynamicProtoChangeDetector(),
|
var pv = new ProtoView(template, new DynamicProtoChangeDetector(null),
|
||||||
new NativeShadowDomStrategy());
|
new NativeShadowDomStrategy());
|
||||||
pv.instantiateInPlace = true;
|
pv.instantiateInPlace = true;
|
||||||
var view = pv.instantiate(null, null);
|
var view = pv.instantiate(null, null);
|
||||||
|
@ -204,7 +204,7 @@ export function main() {
|
||||||
|
|
||||||
it('should be off by default.', () => {
|
it('should be off by default.', () => {
|
||||||
var template = el('<div></div>')
|
var template = el('<div></div>')
|
||||||
var view = new ProtoView(template, new DynamicProtoChangeDetector(),
|
var view = new ProtoView(template, new DynamicProtoChangeDetector(null),
|
||||||
new NativeShadowDomStrategy())
|
new NativeShadowDomStrategy())
|
||||||
.instantiate(null, null);
|
.instantiate(null, null);
|
||||||
view.hydrate(null, null, null);
|
view.hydrate(null, null, null);
|
||||||
|
@ -223,7 +223,7 @@ export function main() {
|
||||||
describe('create ElementInjectors', () => {
|
describe('create ElementInjectors', () => {
|
||||||
it('should use the directives of the ProtoElementInjector', () => {
|
it('should use the directives of the ProtoElementInjector', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
||||||
|
|
||||||
var view = pv.instantiate(null, null);
|
var view = pv.instantiate(null, null);
|
||||||
|
@ -234,7 +234,7 @@ export function main() {
|
||||||
|
|
||||||
it('should use the correct parent', () => {
|
it('should use the correct parent', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
|
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
|
||||||
pv.bindElement(protoParent);
|
pv.bindElement(protoParent);
|
||||||
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
|
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
|
||||||
|
@ -248,7 +248,7 @@ export function main() {
|
||||||
|
|
||||||
it('should not pass the host injector when a parent injector exists', () => {
|
it('should not pass the host injector when a parent injector exists', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
|
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
|
||||||
pv.bindElement(protoParent);
|
pv.bindElement(protoParent);
|
||||||
var testProtoElementInjector = new TestProtoElementInjector(protoParent, 1, [AnotherDirective]);
|
var testProtoElementInjector = new TestProtoElementInjector(protoParent, 1, [AnotherDirective]);
|
||||||
|
@ -264,7 +264,7 @@ export function main() {
|
||||||
|
|
||||||
it('should pass the host injector when there is no parent injector', () => {
|
it('should pass the host injector when there is no parent injector', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
|
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
|
||||||
var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]);
|
var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]);
|
||||||
pv.bindElement(testProtoElementInjector);
|
pv.bindElement(testProtoElementInjector);
|
||||||
|
@ -281,7 +281,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect a single root element injector', () => {
|
it('should collect a single root element injector', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
|
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
|
||||||
pv.bindElement(protoParent);
|
pv.bindElement(protoParent);
|
||||||
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
|
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
|
||||||
|
@ -294,7 +294,7 @@ export function main() {
|
||||||
|
|
||||||
it('should collect multiple root element injectors', () => {
|
it('should collect multiple root element injectors', () => {
|
||||||
var pv = new ProtoView(el('<div><span class="ng-binding"></span><span class="ng-binding"></span></div>'),
|
var pv = new ProtoView(el('<div><span class="ng-binding"></span><span class="ng-binding"></span></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
|
||||||
pv.bindElement(new ProtoElementInjector(null, 2, [AnotherDirective]));
|
pv.bindElement(new ProtoElementInjector(null, 2, [AnotherDirective]));
|
||||||
|
|
||||||
|
@ -312,7 +312,7 @@ export function main() {
|
||||||
|
|
||||||
function createComponentWithSubPV(subProtoView) {
|
function createComponentWithSubPV(subProtoView) {
|
||||||
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'),
|
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'),
|
||||||
new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true));
|
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true));
|
||||||
binder.componentDirective = someComponentDirective;
|
binder.componentDirective = someComponentDirective;
|
||||||
binder.nestedProtoView = subProtoView;
|
binder.nestedProtoView = subProtoView;
|
||||||
|
@ -327,7 +327,7 @@ export function main() {
|
||||||
}
|
}
|
||||||
|
|
||||||
it('should expose component services to the component', () => {
|
it('should expose component services to the component', () => {
|
||||||
var subpv = new ProtoView(el('<span></span>'), new DynamicProtoChangeDetector(), null);
|
var subpv = new ProtoView(el('<span></span>'), new DynamicProtoChangeDetector(null), null);
|
||||||
var pv = createComponentWithSubPV(subpv);
|
var pv = createComponentWithSubPV(subpv);
|
||||||
|
|
||||||
var view = createNestedView(pv);
|
var view = createNestedView(pv);
|
||||||
|
@ -340,7 +340,7 @@ export function main() {
|
||||||
() => {
|
() => {
|
||||||
var subpv = new ProtoView(
|
var subpv = new ProtoView(
|
||||||
el('<div dec class="ng-binding">hello shadow dom</div>'),
|
el('<div dec class="ng-binding">hello shadow dom</div>'),
|
||||||
new DynamicProtoChangeDetector(),
|
new DynamicProtoChangeDetector(null),
|
||||||
null);
|
null);
|
||||||
subpv.bindElement(
|
subpv.bindElement(
|
||||||
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
|
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
|
||||||
|
@ -365,7 +365,7 @@ export function main() {
|
||||||
it('dehydration should dehydrate child component views too', () => {
|
it('dehydration should dehydrate child component views too', () => {
|
||||||
var subpv = new ProtoView(
|
var subpv = new ProtoView(
|
||||||
el('<div dec class="ng-binding">hello shadow dom</div>'),
|
el('<div dec class="ng-binding">hello shadow dom</div>'),
|
||||||
new DynamicProtoChangeDetector(),
|
new DynamicProtoChangeDetector(null),
|
||||||
null);
|
null);
|
||||||
subpv.bindElement(
|
subpv.bindElement(
|
||||||
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
|
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
|
||||||
|
@ -382,7 +382,7 @@ export function main() {
|
||||||
|
|
||||||
it('should create shadow dom (Native Strategy)', () => {
|
it('should create shadow dom (Native Strategy)', () => {
|
||||||
var subpv = new ProtoView(el('<span>hello shadow dom</span>'),
|
var subpv = new ProtoView(el('<span>hello shadow dom</span>'),
|
||||||
new DynamicProtoChangeDetector(),
|
new DynamicProtoChangeDetector(null),
|
||||||
null);
|
null);
|
||||||
var pv = createComponentWithSubPV(subpv);
|
var pv = createComponentWithSubPV(subpv);
|
||||||
|
|
||||||
|
@ -393,10 +393,10 @@ export function main() {
|
||||||
|
|
||||||
it('should emulate shadow dom (Emulated Strategy)', () => {
|
it('should emulate shadow dom (Emulated Strategy)', () => {
|
||||||
var subpv = new ProtoView(el('<span>hello shadow dom</span>'),
|
var subpv = new ProtoView(el('<span>hello shadow dom</span>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
|
|
||||||
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'),
|
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'),
|
||||||
new DynamicProtoChangeDetector(), new EmulatedShadowDomStrategy());
|
new DynamicProtoChangeDetector(null), new EmulatedShadowDomStrategy());
|
||||||
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true));
|
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true));
|
||||||
binder.componentDirective = new DirectiveMetadataReader().read(SomeComponent);
|
binder.componentDirective = new DirectiveMetadataReader().read(SomeComponent);
|
||||||
binder.nestedProtoView = subpv;
|
binder.nestedProtoView = subpv;
|
||||||
|
@ -410,9 +410,9 @@ export function main() {
|
||||||
describe('with template views', () => {
|
describe('with template views', () => {
|
||||||
function createViewWithViewport() {
|
function createViewWithViewport() {
|
||||||
var templateProtoView = new ProtoView(
|
var templateProtoView = new ProtoView(
|
||||||
el('<div id="1"></div>'), new DynamicProtoChangeDetector(), null);
|
el('<div id="1"></div>'), new DynamicProtoChangeDetector(null), null);
|
||||||
var pv = new ProtoView(el('<someTmpl class="ng-binding"></someTmpl>'),
|
var pv = new ProtoView(el('<someTmpl class="ng-binding"></someTmpl>'),
|
||||||
new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeViewport]));
|
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeViewport]));
|
||||||
binder.viewportDirective = someViewportDirective;
|
binder.viewportDirective = someViewportDirective;
|
||||||
binder.nestedProtoView = templateProtoView;
|
binder.nestedProtoView = templateProtoView;
|
||||||
|
@ -456,7 +456,7 @@ export function main() {
|
||||||
|
|
||||||
function createProtoView() {
|
function createProtoView() {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(new TestProtoElementInjector(null, 0, []));
|
pv.bindElement(new TestProtoElementInjector(null, 0, []));
|
||||||
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
|
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
|
||||||
return pv;
|
return pv;
|
||||||
|
@ -491,7 +491,7 @@ export function main() {
|
||||||
|
|
||||||
it('should support custom event emitters', () => {
|
it('should support custom event emitters', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(new TestProtoElementInjector(null, 0, [EventEmitterDirective]));
|
pv.bindElement(new TestProtoElementInjector(null, 0, [EventEmitterDirective]));
|
||||||
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
|
pv.bindEvent('click', parser.parseBinding('callMe($event)', null));
|
||||||
|
|
||||||
|
@ -522,7 +522,7 @@ export function main() {
|
||||||
|
|
||||||
it('should consume text node changes', () => {
|
it('should consume text node changes', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding">{{}}</div>'),
|
var pv = new ProtoView(el('<div class="ng-binding">{{}}</div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(null);
|
pv.bindElement(null);
|
||||||
pv.bindTextNode(0, parser.parseBinding('foo', null));
|
pv.bindTextNode(0, parser.parseBinding('foo', null));
|
||||||
createViewAndChangeDetector(pv);
|
createViewAndChangeDetector(pv);
|
||||||
|
@ -534,7 +534,7 @@ export function main() {
|
||||||
|
|
||||||
it('should consume element binding changes', () => {
|
it('should consume element binding changes', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(null);
|
pv.bindElement(null);
|
||||||
pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id'));
|
pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id'));
|
||||||
createViewAndChangeDetector(pv);
|
createViewAndChangeDetector(pv);
|
||||||
|
@ -546,7 +546,7 @@ export function main() {
|
||||||
|
|
||||||
it('should consume directive watch expression change', () => {
|
it('should consume directive watch expression change', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
|
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
|
||||||
pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'), false);
|
pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'), false);
|
||||||
createViewAndChangeDetector(pv);
|
createViewAndChangeDetector(pv);
|
||||||
|
@ -558,7 +558,7 @@ export function main() {
|
||||||
|
|
||||||
it('should notify a directive about changes after all its properties have been set', () => {
|
it('should notify a directive about changes after all its properties have been set', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
|
|
||||||
pv.bindElement(new ProtoElementInjector(null, 0, [
|
pv.bindElement(new ProtoElementInjector(null, 0, [
|
||||||
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
|
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
|
||||||
|
@ -577,7 +577,7 @@ export function main() {
|
||||||
|
|
||||||
it('should provide a map of updated properties', () => {
|
it('should provide a map of updated properties', () => {
|
||||||
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||||
new DynamicProtoChangeDetector(), null);
|
new DynamicProtoChangeDetector(null), null);
|
||||||
|
|
||||||
pv.bindElement(new ProtoElementInjector(null, 0, [
|
pv.bindElement(new ProtoElementInjector(null, 0, [
|
||||||
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
|
DirectiveBinding.createFromType(DirectiveImplementingOnChange, new Directive({lifecycle: [onChange]}))
|
||||||
|
@ -604,13 +604,13 @@ export function main() {
|
||||||
var element, pv;
|
var element, pv;
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
element = DOM.createElement('div');
|
element = DOM.createElement('div');
|
||||||
pv = new ProtoView(el('<div>hi</div>'), new DynamicProtoChangeDetector(),
|
pv = new ProtoView(el('<div>hi</div>'), new DynamicProtoChangeDetector(null),
|
||||||
new NativeShadowDomStrategy());
|
new NativeShadowDomStrategy());
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create the root component when instantiated', () => {
|
it('should create the root component when instantiated', () => {
|
||||||
var rootProtoView = ProtoView.createRootProtoView(pv, element,
|
var rootProtoView = ProtoView.createRootProtoView(pv, element,
|
||||||
someComponentDirective, new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
someComponentDirective, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
var view = rootProtoView.instantiate(null, null);
|
var view = rootProtoView.instantiate(null, null);
|
||||||
view.hydrate(new Injector([]), null, null);
|
view.hydrate(new Injector([]), null, null);
|
||||||
expect(view.rootElementInjectors[0].get(SomeComponent)).not.toBe(null);
|
expect(view.rootElementInjectors[0].get(SomeComponent)).not.toBe(null);
|
||||||
|
@ -618,7 +618,7 @@ export function main() {
|
||||||
|
|
||||||
it('should inject the protoView into the shadowDom', () => {
|
it('should inject the protoView into the shadowDom', () => {
|
||||||
var rootProtoView = ProtoView.createRootProtoView(pv, element,
|
var rootProtoView = ProtoView.createRootProtoView(pv, element,
|
||||||
someComponentDirective, new DynamicProtoChangeDetector(), new NativeShadowDomStrategy());
|
someComponentDirective, new DynamicProtoChangeDetector(null), new NativeShadowDomStrategy());
|
||||||
var view = rootProtoView.instantiate(null, null);
|
var view = rootProtoView.instantiate(null, null);
|
||||||
view.hydrate(new Injector([]), null, null);
|
view.hydrate(new Injector([]), null, null);
|
||||||
expect(element.shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hi');
|
expect(element.shadowRoot.childNodes[0].childNodes[0].nodeValue).toEqual('hi');
|
||||||
|
|
|
@ -44,7 +44,7 @@ export function setupReflector() {
|
||||||
reflector.registerGetters({
|
reflector.registerGetters({
|
||||||
'scrollAreas': (o) => o.scrollAreas,
|
'scrollAreas': (o) => o.scrollAreas,
|
||||||
'length': (o) => o.length,
|
'length': (o) => o.length,
|
||||||
'iterable': (o) => o.iterable,
|
'iterableChanges': (o) => o.iterableChanges,
|
||||||
'scrollArea': (o) => o.scrollArea,
|
'scrollArea': (o) => o.scrollArea,
|
||||||
'item': (o) => o.item,
|
'item': (o) => o.item,
|
||||||
'visibleItems': (o) => o.visibleItems,
|
'visibleItems': (o) => o.visibleItems,
|
||||||
|
@ -95,7 +95,7 @@ export function setupReflector() {
|
||||||
'scrollArea': (o, v) => o.scrollArea = v,
|
'scrollArea': (o, v) => o.scrollArea = v,
|
||||||
'item': (o, v) => o.item = v,
|
'item': (o, v) => o.item = v,
|
||||||
'visibleItems': (o, v) => o.visibleItems = v,
|
'visibleItems': (o, v) => o.visibleItems = v,
|
||||||
'iterable': (o, v) => o.iterable = v,
|
'iterableChanges': (o, v) => o.iterableChanges = v,
|
||||||
'width': (o, v) => o.width = v,
|
'width': (o, v) => o.width = v,
|
||||||
'value': (o, v) => o.value = v,
|
'value': (o, v) => o.value = v,
|
||||||
'company': (o, v) => o.company = v,
|
'company': (o, v) => o.company = v,
|
||||||
|
@ -167,9 +167,8 @@ export function setupReflectorForAngular() {
|
||||||
'parameters': [[ViewContainer]],
|
'parameters': [[ViewContainer]],
|
||||||
'annotations' : [new Viewport({
|
'annotations' : [new Viewport({
|
||||||
selector: '[foreach]',
|
selector: '[foreach]',
|
||||||
lifecycle: [onChange],
|
|
||||||
bind: {
|
bind: {
|
||||||
'in': 'iterable[]'
|
'in': 'iterableChanges[]'
|
||||||
}
|
}
|
||||||
})]
|
})]
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue