feat(change_detection): added a directive lifecycle hook that is called after children are checked
This commit is contained in:
parent
507f7ea70a
commit
723e8fde93
|
@ -42,11 +42,13 @@ export class AbstractChangeDetector extends ChangeDetector {
|
||||||
|
|
||||||
this.detectChangesInRecords(throwOnChange);
|
this.detectChangesInRecords(throwOnChange);
|
||||||
this._detectChangesInChildren(throwOnChange);
|
this._detectChangesInChildren(throwOnChange);
|
||||||
|
this.notifyOnAllChangesDone();
|
||||||
|
|
||||||
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
|
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
|
||||||
}
|
}
|
||||||
|
|
||||||
detectChangesInRecords(throwOnChange:boolean){}
|
detectChangesInRecords(throwOnChange:boolean){}
|
||||||
|
notifyOnAllChangesDone(){}
|
||||||
|
|
||||||
_detectChangesInChildren(throwOnChange:boolean) {
|
_detectChangesInChildren(throwOnChange:boolean) {
|
||||||
var children = this.children;
|
var children = this.children;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
library change_detectoin.change_detection_jit_generator;
|
library change_detectoin.change_detection_jit_generator;
|
||||||
|
|
||||||
class ChangeDetectorJITGenerator {
|
class ChangeDetectorJITGenerator {
|
||||||
ChangeDetectorJITGenerator(typeName, records) {
|
ChangeDetectorJITGenerator(typeName, records, directiveMementos) {
|
||||||
}
|
}
|
||||||
|
|
||||||
generate() {
|
generate() {
|
||||||
|
|
|
@ -64,6 +64,7 @@ import {
|
||||||
* }
|
* }
|
||||||
* }
|
* }
|
||||||
*
|
*
|
||||||
|
* ChangeDetector0.prototype.notifyOnAllChangesDone = function() {}
|
||||||
*
|
*
|
||||||
* ChangeDetector0.prototype.hydrate = function(context, locals) {
|
* ChangeDetector0.prototype.hydrate = function(context, locals) {
|
||||||
* this.context = context;
|
* this.context = context;
|
||||||
|
@ -96,31 +97,35 @@ var UTIL = "ChangeDetectionUtil";
|
||||||
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
var DISPATCHER_ACCESSOR = "this.dispatcher";
|
||||||
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
var PIPE_REGISTRY_ACCESSOR = "this.pipeRegistry";
|
||||||
var PROTOS_ACCESSOR = "this.protos";
|
var PROTOS_ACCESSOR = "this.protos";
|
||||||
|
var MEMENTOS_ACCESSOR = "this.directiveMementos";
|
||||||
var CONTEXT_ACCESSOR = "this.context";
|
var CONTEXT_ACCESSOR = "this.context";
|
||||||
var CHANGE_LOCAL = "change";
|
var CHANGE_LOCAL = "change";
|
||||||
var CHANGES_LOCAL = "changes";
|
var CHANGES_LOCAL = "changes";
|
||||||
var LOCALS_ACCESSOR = "this.locals";
|
var LOCALS_ACCESSOR = "this.locals";
|
||||||
var TEMP_LOCAL = "temp";
|
var TEMP_LOCAL = "temp";
|
||||||
|
|
||||||
function typeTemplate(type:string, cons:string, detectChanges:string, setContext:string):string {
|
function typeTemplate(type:string, cons:string, detectChanges:string,
|
||||||
|
notifyOnAllChangesDone:string, setContext:string):string {
|
||||||
return `
|
return `
|
||||||
${cons}
|
${cons}
|
||||||
${detectChanges}
|
${detectChanges}
|
||||||
|
${notifyOnAllChangesDone}
|
||||||
${setContext};
|
${setContext};
|
||||||
|
|
||||||
return function(dispatcher, pipeRegistry) {
|
return function(dispatcher, pipeRegistry) {
|
||||||
return new ${type}(dispatcher, pipeRegistry, protos);
|
return new ${type}(dispatcher, pipeRegistry, protos, directiveMementos);
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
function constructorTemplate(type:string, fieldsDefinitions:string):string {
|
||||||
return `
|
return `
|
||||||
var ${type} = function ${type}(dispatcher, pipeRegistry, protos) {
|
var ${type} = function ${type}(dispatcher, pipeRegistry, protos, directiveMementos) {
|
||||||
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
${ABSTRACT_CHANGE_DETECTOR}.call(this);
|
||||||
${DISPATCHER_ACCESSOR} = dispatcher;
|
${DISPATCHER_ACCESSOR} = dispatcher;
|
||||||
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
${PIPE_REGISTRY_ACCESSOR} = pipeRegistry;
|
||||||
${PROTOS_ACCESSOR} = protos;
|
${PROTOS_ACCESSOR} = protos;
|
||||||
|
${MEMENTOS_ACCESSOR} = directiveMementos;
|
||||||
${fieldsDefinitions}
|
${fieldsDefinitions}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,6 +162,18 @@ ${type}.prototype.detectChangesInRecords = function(throwOnChange) {
|
||||||
`;
|
`;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function notifyOnAllChangesDoneTemplate(type:string, body:string):string {
|
||||||
|
return `
|
||||||
|
${type}.prototype.notifyOnAllChangesDone = function() {
|
||||||
|
${body}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function onAllChangesDoneTemplate(index:number):string {
|
||||||
|
return `${DISPATCHER_ACCESSOR}.onAllChangesDone(${MEMENTOS_ACCESSOR}[${index}]);`;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
|
function bodyTemplate(localDefinitions:string, changeDefinitions:string, records:string):string {
|
||||||
return `
|
return `
|
||||||
|
@ -247,14 +264,16 @@ function addSimpleChangeRecordTemplate(protoIndex:number, oldValue:string, newVa
|
||||||
export class ChangeDetectorJITGenerator {
|
export class ChangeDetectorJITGenerator {
|
||||||
typeName:string;
|
typeName:string;
|
||||||
records:List<ProtoRecord>;
|
records:List<ProtoRecord>;
|
||||||
|
directiveMementos:List;
|
||||||
localNames:List<String>;
|
localNames:List<String>;
|
||||||
changeNames:List<String>;
|
changeNames:List<String>;
|
||||||
fieldNames:List<String>;
|
fieldNames:List<String>;
|
||||||
pipeNames:List<String>;
|
pipeNames:List<String>;
|
||||||
|
|
||||||
constructor(typeName:string, records:List<ProtoRecord>) {
|
constructor(typeName:string, records:List<ProtoRecord>, directiveMementos:List) {
|
||||||
this.typeName = typeName;
|
this.typeName = typeName;
|
||||||
this.records = records;
|
this.records = records;
|
||||||
|
this.directiveMementos = directiveMementos;
|
||||||
|
|
||||||
this.localNames = this.getLocalNames(records);
|
this.localNames = this.getLocalNames(records);
|
||||||
this.changeNames = this.getChangeNames(this.localNames);
|
this.changeNames = this.getChangeNames(this.localNames);
|
||||||
|
@ -284,8 +303,10 @@ export class ChangeDetectorJITGenerator {
|
||||||
}
|
}
|
||||||
|
|
||||||
generate():Function {
|
generate():Function {
|
||||||
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(), this.genHydrate());
|
var text = typeTemplate(this.typeName, this.genConstructor(), this.genDetectChanges(),
|
||||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', text)(AbstractChangeDetector, ChangeDetectionUtil, this.records);
|
this.genNotifyOnAllChangesDone(), this.genHydrate());
|
||||||
|
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos', 'directiveMementos', text)
|
||||||
|
(AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveMementos);
|
||||||
}
|
}
|
||||||
|
|
||||||
genConstructor():string {
|
genConstructor():string {
|
||||||
|
@ -319,6 +340,20 @@ export class ChangeDetectorJITGenerator {
|
||||||
return detectChangesTemplate(this.typeName, body);
|
return detectChangesTemplate(this.typeName, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
genNotifyOnAllChangesDone():string {
|
||||||
|
var notifications = [];
|
||||||
|
var mementos = this.directiveMementos;
|
||||||
|
|
||||||
|
for (var i = mementos.length - 1; i >= 0; --i) {
|
||||||
|
var memento = mementos[i];
|
||||||
|
if (memento.notifyOnAllChangesDone) {
|
||||||
|
notifications.push(onAllChangesDoneTemplate(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return notifyOnAllChangesDoneTemplate(this.typeName, notifications.join(";\n"));
|
||||||
|
}
|
||||||
|
|
||||||
genBody():string {
|
genBody():string {
|
||||||
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
var rec = this.records.map((r) => this.genRecord(r)).join("\n");
|
||||||
return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
return bodyTemplate(this.genLocalDefinitions(), this.genChangeDefinitions(), rec);
|
||||||
|
|
|
@ -34,8 +34,9 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
prevContexts:List;
|
prevContexts:List;
|
||||||
|
|
||||||
protos:List<ProtoRecord>;
|
protos:List<ProtoRecord>;
|
||||||
|
directiveMementos:List;
|
||||||
|
|
||||||
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>) {
|
constructor(dispatcher:any, pipeRegistry:PipeRegistry, protoRecords:List<ProtoRecord>, directiveMementos:List) {
|
||||||
super();
|
super();
|
||||||
this.dispatcher = dispatcher;
|
this.dispatcher = dispatcher;
|
||||||
this.pipeRegistry = pipeRegistry;
|
this.pipeRegistry = pipeRegistry;
|
||||||
|
@ -52,6 +53,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
this.locals = null;
|
this.locals = null;
|
||||||
|
|
||||||
this.protos = protoRecords;
|
this.protos = protoRecords;
|
||||||
|
this.directiveMementos = directiveMementos;
|
||||||
}
|
}
|
||||||
|
|
||||||
hydrate(context:any, locals:any) {
|
hydrate(context:any, locals:any) {
|
||||||
|
@ -102,6 +104,16 @@ export class DynamicChangeDetector extends AbstractChangeDetector {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
notifyOnAllChangesDone() {
|
||||||
|
var mementos = this.directiveMementos;
|
||||||
|
for (var i = mementos.length - 1; i >= 0; --i) {
|
||||||
|
var memento = mementos[i];
|
||||||
|
if (memento.notifyOnAllChangesDone) {
|
||||||
|
this.dispatcher.onAllChangesDone(memento);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_check(proto:ProtoRecord) {
|
_check(proto:ProtoRecord) {
|
||||||
try {
|
try {
|
||||||
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
|
if (proto.mode === RECORD_TYPE_PIPE || proto.mode === RECORD_TYPE_BINDING_PIPE) {
|
||||||
|
|
|
@ -47,7 +47,7 @@ import {
|
||||||
|
|
||||||
export class ProtoChangeDetector {
|
export class ProtoChangeDetector {
|
||||||
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
addAst(ast:AST, bindingMemento:any, directiveMemento:any = null){}
|
||||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List):ChangeDetector{
|
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMemento:List):ChangeDetector{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,9 +73,9 @@ export class DynamicProtoChangeDetector extends ProtoChangeDetector {
|
||||||
this._pipeRegistry = pipeRegistry;
|
this._pipeRegistry = pipeRegistry;
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) {
|
||||||
this._createRecordsIfNecessary(bindingRecords, variableBindings);
|
this._createRecordsIfNecessary(bindingRecords, variableBindings);
|
||||||
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records);
|
return new DynamicChangeDetector(dispatcher, this._pipeRegistry, this._records, directiveMementos);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
|
_createRecordsIfNecessary(bindingRecords:List, variableBindings:List) {
|
||||||
|
@ -100,12 +100,12 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
this._factory = null;
|
this._factory = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List) {
|
instantiate(dispatcher:any, bindingRecords:List, variableBindings:List, directiveMementos:List) {
|
||||||
this._createFactoryIfNecessary(bindingRecords, variableBindings);
|
this._createFactoryIfNecessary(bindingRecords, variableBindings, directiveMementos);
|
||||||
return this._factory(dispatcher, this._pipeRegistry);
|
return this._factory(dispatcher, this._pipeRegistry);
|
||||||
}
|
}
|
||||||
|
|
||||||
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List) {
|
_createFactoryIfNecessary(bindingRecords:List, variableBindings:List, directiveMementos:List) {
|
||||||
if (isBlank(this._factory)) {
|
if (isBlank(this._factory)) {
|
||||||
var recordBuilder = new ProtoRecordBuilder();
|
var recordBuilder = new ProtoRecordBuilder();
|
||||||
ListWrapper.forEach(bindingRecords, (r) => {
|
ListWrapper.forEach(bindingRecords, (r) => {
|
||||||
|
@ -114,7 +114,7 @@ export class JitProtoChangeDetector extends ProtoChangeDetector {
|
||||||
var c = _jitProtoChangeDetectorClassCounter++;
|
var c = _jitProtoChangeDetectorClassCounter++;
|
||||||
var records = coalesce(recordBuilder.records);
|
var records = coalesce(recordBuilder.records);
|
||||||
var typeName = `ChangeDetector${c}`;
|
var typeName = `ChangeDetector${c}`;
|
||||||
this._factory = new ChangeDetectorJITGenerator(typeName, records).generate();
|
this._factory = new ChangeDetectorJITGenerator(typeName, records, directiveMementos).generate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -900,3 +900,23 @@ export const onDestroy = "onDestroy";
|
||||||
* @publicModule angular2/annotations
|
* @publicModule angular2/annotations
|
||||||
*/
|
*/
|
||||||
export const onChange = "onChange";
|
export const onChange = "onChange";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Notify a directive when the bindings of all its children have been changed.
|
||||||
|
*
|
||||||
|
* ## Example:
|
||||||
|
*
|
||||||
|
* ```
|
||||||
|
* @Decorator({
|
||||||
|
* selector: '[class-set]',
|
||||||
|
* })
|
||||||
|
* class ClassSet {
|
||||||
|
*
|
||||||
|
* onAllChangesDone() {
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* }
|
||||||
|
* ```
|
||||||
|
* @publicModule angular2/annotations
|
||||||
|
*/
|
||||||
|
export const onAllChangesDone = "onAllChangesDone";
|
||||||
|
|
|
@ -7,7 +7,7 @@ import {EventEmitter, PropertySetter, Attribute} from 'angular2/src/core/annotat
|
||||||
import * as viewModule from 'angular2/src/core/compiler/view';
|
import * as viewModule from 'angular2/src/core/compiler/view';
|
||||||
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
import {ViewContainer} from 'angular2/src/core/compiler/view_container';
|
||||||
import {NgElement} from 'angular2/src/core/dom/element';
|
import {NgElement} from 'angular2/src/core/dom/element';
|
||||||
import {Directive, onChange, onDestroy} from 'angular2/src/core/annotations/annotations';
|
import {Directive, onChange, onDestroy, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
||||||
import {BindingPropagationConfig} from 'angular2/change_detection';
|
import {BindingPropagationConfig} from 'angular2/change_detection';
|
||||||
import * as pclModule from 'angular2/src/core/compiler/private_component_location';
|
import * as pclModule from 'angular2/src/core/compiler/private_component_location';
|
||||||
import {setterFactory} from './property_setter_factory';
|
import {setterFactory} from './property_setter_factory';
|
||||||
|
@ -131,11 +131,13 @@ export class DirectiveDependency extends Dependency {
|
||||||
export class DirectiveBinding extends Binding {
|
export class DirectiveBinding extends Binding {
|
||||||
callOnDestroy:boolean;
|
callOnDestroy:boolean;
|
||||||
callOnChange:boolean;
|
callOnChange:boolean;
|
||||||
|
callOnAllChangesDone:boolean;
|
||||||
|
|
||||||
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
constructor(key:Key, factory:Function, dependencies:List, providedAsPromise:boolean, annotation:Directive) {
|
||||||
super(key, factory, dependencies, providedAsPromise);
|
super(key, factory, dependencies, providedAsPromise);
|
||||||
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
|
this.callOnDestroy = isPresent(annotation) && annotation.hasLifecycleHook(onDestroy);
|
||||||
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
this.callOnChange = isPresent(annotation) && annotation.hasLifecycleHook(onChange);
|
||||||
|
this.callOnAllChangesDone = isPresent(annotation) && annotation.hasLifecycleHook(onAllChangesDone);
|
||||||
}
|
}
|
||||||
|
|
||||||
static createFromBinding(b:Binding, annotation:Directive):Binding {
|
static createFromBinding(b:Binding, annotation:Directive):Binding {
|
||||||
|
@ -216,6 +218,8 @@ export class ProtoElementInjector {
|
||||||
distanceToParent:number;
|
distanceToParent:number;
|
||||||
attributes:Map;
|
attributes:Map;
|
||||||
|
|
||||||
|
numberOfDirectives:number;
|
||||||
|
|
||||||
/** Whether the element is exported as $implicit. */
|
/** Whether the element is exported as $implicit. */
|
||||||
exportElement:boolean;
|
exportElement:boolean;
|
||||||
|
|
||||||
|
@ -244,6 +248,7 @@ export class ProtoElementInjector {
|
||||||
this._binding8 = null; this._keyId8 = null;
|
this._binding8 = null; this._keyId8 = null;
|
||||||
this._binding9 = null; this._keyId9 = null;
|
this._binding9 = null; this._keyId9 = null;
|
||||||
|
|
||||||
|
this.numberOfDirectives = bindings.length;
|
||||||
var length = bindings.length;
|
var length = bindings.length;
|
||||||
|
|
||||||
if (length > 0) {this._binding0 = this._createBinding(bindings[0]); this._keyId0 = this._binding0.key.id;}
|
if (length > 0) {this._binding0 = this._createBinding(bindings[0]); this._keyId0 = this._binding0.key.id;}
|
||||||
|
@ -282,6 +287,20 @@ export class ProtoElementInjector {
|
||||||
return isPresent(this._binding0);
|
return isPresent(this._binding0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDirectiveBindingAtIndex(index:int) {
|
||||||
|
if (index == 0) return this._binding0;
|
||||||
|
if (index == 1) return this._binding1;
|
||||||
|
if (index == 2) return this._binding2;
|
||||||
|
if (index == 3) return this._binding3;
|
||||||
|
if (index == 4) return this._binding4;
|
||||||
|
if (index == 5) return this._binding5;
|
||||||
|
if (index == 6) return this._binding6;
|
||||||
|
if (index == 7) return this._binding7;
|
||||||
|
if (index == 8) return this._binding8;
|
||||||
|
if (index == 9) return this._binding9;
|
||||||
|
throw new OutOfBoundsAccess(index);
|
||||||
|
}
|
||||||
|
|
||||||
hasEventEmitter(eventName: string) {
|
hasEventEmitter(eventName: string) {
|
||||||
var p = this;
|
var p = this;
|
||||||
if (isPresent(p._binding0) && DirectiveBinding._hasEventEmitter(eventName, p._binding0)) return true;
|
if (isPresent(p._binding0) && DirectiveBinding._hasEventEmitter(eventName, p._binding0)) return true;
|
||||||
|
@ -648,18 +667,7 @@ export class ElementInjector extends TreeNode {
|
||||||
}
|
}
|
||||||
|
|
||||||
getDirectiveBindingAtIndex(index:int) {
|
getDirectiveBindingAtIndex(index:int) {
|
||||||
var p = this._proto;
|
return this._proto.getDirectiveBindingAtIndex(index);
|
||||||
if (index == 0) return p._binding0;
|
|
||||||
if (index == 1) return p._binding1;
|
|
||||||
if (index == 2) return p._binding2;
|
|
||||||
if (index == 3) return p._binding3;
|
|
||||||
if (index == 4) return p._binding4;
|
|
||||||
if (index == 5) return p._binding5;
|
|
||||||
if (index == 6) return p._binding6;
|
|
||||||
if (index == 7) return p._binding7;
|
|
||||||
if (index == 8) return p._binding8;
|
|
||||||
if (index == 9) return p._binding9;
|
|
||||||
throw new OutOfBoundsAccess(index);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hasInstances() {
|
hasInstances() {
|
||||||
|
|
|
@ -236,6 +236,11 @@ export class View {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAllChangesDone(directiveMemento) {
|
||||||
|
var dir = directiveMemento.directive(this.elementInjectors);
|
||||||
|
dir.onAllChangesDone();
|
||||||
|
}
|
||||||
|
|
||||||
_invokeMementos(records:List) {
|
_invokeMementos(records:List) {
|
||||||
for(var i = 0; i < records.length; ++i) {
|
for(var i = 0; i < records.length; ++i) {
|
||||||
this._invokeMementoFor(records[i]);
|
this._invokeMementoFor(records[i]);
|
||||||
|
@ -303,6 +308,9 @@ export class ProtoView {
|
||||||
parentProtoView:ProtoView;
|
parentProtoView:ProtoView;
|
||||||
_variableBindings:List;
|
_variableBindings:List;
|
||||||
|
|
||||||
|
_directiveMementosMap:Map;
|
||||||
|
_directiveMementos:List;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
template,
|
template,
|
||||||
protoChangeDetector:ProtoChangeDetector,
|
protoChangeDetector:ProtoChangeDetector,
|
||||||
|
@ -324,7 +332,9 @@ export class ProtoView {
|
||||||
this.stylePromises = [];
|
this.stylePromises = [];
|
||||||
this.eventHandlers = [];
|
this.eventHandlers = [];
|
||||||
this.bindingRecords = [];
|
this.bindingRecords = [];
|
||||||
|
this._directiveMementosMap = MapWrapper.create();
|
||||||
this._variableBindings = null;
|
this._variableBindings = null;
|
||||||
|
this._directiveMementos = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
// TODO(rado): hostElementInjector should be moved to hydrate phase.
|
||||||
|
@ -357,6 +367,27 @@ export class ProtoView {
|
||||||
return this._variableBindings;
|
return this._variableBindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this work should be done the constructor of ProtoView once we separate
|
||||||
|
// ProtoView and ProtoViewBuilder
|
||||||
|
_getDirectiveMementos() {
|
||||||
|
if (isPresent(this._directiveMementos)) {
|
||||||
|
return this._directiveMementos;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._directiveMementos = [];
|
||||||
|
|
||||||
|
for (var injectorIndex = 0; injectorIndex < this.elementBinders.length; ++injectorIndex) {
|
||||||
|
var pei = this.elementBinders[injectorIndex].protoElementInjector;
|
||||||
|
if (isPresent(pei)) {
|
||||||
|
for (var directiveIndex = 0; directiveIndex < pei.numberOfDirectives; ++directiveIndex) {
|
||||||
|
ListWrapper.push(this._directiveMementos, this._getDirectiveMemento(injectorIndex, directiveIndex));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return this._directiveMementos;
|
||||||
|
}
|
||||||
|
|
||||||
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
|
_instantiate(hostElementInjector: ElementInjector, eventManager: EventManager): View {
|
||||||
var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element);
|
var rootElementClone = this.instantiateInPlace ? this.element : DOM.importIntoDoc(this.element);
|
||||||
var elementsWithBindingsDynamic;
|
var elementsWithBindingsDynamic;
|
||||||
|
@ -385,7 +416,9 @@ export class ProtoView {
|
||||||
}
|
}
|
||||||
|
|
||||||
var view = new View(this, viewNodes, this.protoLocals);
|
var view = new View(this, viewNodes, this.protoLocals);
|
||||||
var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords, this._getVariableBindings());
|
var changeDetector = this.protoChangeDetector.instantiate(view, this.bindingRecords,
|
||||||
|
this._getVariableBindings(), this._getDirectiveMementos());
|
||||||
|
|
||||||
var binders = this.elementBinders;
|
var binders = this.elementBinders;
|
||||||
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
var elementInjectors = ListWrapper.createFixedSize(binders.length);
|
||||||
var eventHandlers = ListWrapper.createFixedSize(binders.length);
|
var eventHandlers = ListWrapper.createFixedSize(binders.length);
|
||||||
|
@ -610,16 +643,30 @@ export class ProtoView {
|
||||||
setterName:string,
|
setterName:string,
|
||||||
setter:SetterFn) {
|
setter:SetterFn) {
|
||||||
|
|
||||||
|
var elementIndex = this.elementBinders.length-1;
|
||||||
var bindingMemento = new DirectiveBindingMemento(
|
var bindingMemento = new DirectiveBindingMemento(
|
||||||
this.elementBinders.length-1,
|
elementIndex,
|
||||||
directiveIndex,
|
directiveIndex,
|
||||||
setterName,
|
setterName,
|
||||||
setter
|
setter
|
||||||
);
|
);
|
||||||
var directiveMemento = DirectiveMemento.get(bindingMemento);
|
var directiveMemento = this._getDirectiveMemento(elementIndex, directiveIndex);
|
||||||
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, bindingMemento, directiveMemento));
|
ListWrapper.push(this.bindingRecords, new BindingRecord(expression, bindingMemento, directiveMemento));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_getDirectiveMemento(elementInjectorIndex:number, directiveIndex:number) {
|
||||||
|
var id = elementInjectorIndex * 100 + directiveIndex;
|
||||||
|
var protoElementInjector = this.elementBinders[elementInjectorIndex].protoElementInjector;
|
||||||
|
|
||||||
|
if (!MapWrapper.contains(this._directiveMementosMap, id)) {
|
||||||
|
var binding = protoElementInjector.getDirectiveBindingAtIndex(directiveIndex);
|
||||||
|
MapWrapper.set(this._directiveMementosMap, id,
|
||||||
|
new DirectiveMemento(elementInjectorIndex, directiveIndex, binding.callOnAllChangesDone));
|
||||||
|
}
|
||||||
|
|
||||||
|
return MapWrapper.get(this._directiveMementosMap, id);
|
||||||
|
}
|
||||||
|
|
||||||
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
|
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
|
||||||
// and the component template is already compiled into protoView.
|
// and the component template is already compiled into protoView.
|
||||||
// Used for bootstrapping.
|
// Used for bootstrapping.
|
||||||
|
@ -688,26 +735,15 @@ export class DirectiveBindingMemento {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var _directiveMementos = MapWrapper.create();
|
|
||||||
|
|
||||||
class DirectiveMemento {
|
class DirectiveMemento {
|
||||||
_elementInjectorIndex:number;
|
_elementInjectorIndex:number;
|
||||||
_directiveIndex:number;
|
_directiveIndex:number;
|
||||||
|
notifyOnAllChangesDone:boolean;
|
||||||
|
|
||||||
constructor(elementInjectorIndex:number, directiveIndex:number) {
|
constructor(elementInjectorIndex:number, directiveIndex:number, notifyOnAllChangesDone:boolean) {
|
||||||
this._elementInjectorIndex = elementInjectorIndex;
|
this._elementInjectorIndex = elementInjectorIndex;
|
||||||
this._directiveIndex = directiveIndex;
|
this._directiveIndex = directiveIndex;
|
||||||
}
|
this.notifyOnAllChangesDone = notifyOnAllChangesDone;
|
||||||
|
|
||||||
static get(memento:DirectiveBindingMemento) {
|
|
||||||
var elementInjectorIndex = memento._elementInjectorIndex;
|
|
||||||
var directiveIndex = memento._directiveIndex;
|
|
||||||
var id = elementInjectorIndex * 100 + directiveIndex;
|
|
||||||
|
|
||||||
if (!MapWrapper.contains(_directiveMementos, id)) {
|
|
||||||
MapWrapper.set(_directiveMementos, id, new DirectiveMemento(elementInjectorIndex, directiveIndex));
|
|
||||||
}
|
|
||||||
return MapWrapper.get(_directiveMementos, id);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
directive(elementInjectors:List<ElementInjector>) {
|
directive(elementInjectors:List<ElementInjector>) {
|
||||||
|
|
|
@ -44,7 +44,8 @@ export function main() {
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
|
|
||||||
var variableBindings = convertLocalsToVariableBindings(locals);
|
var variableBindings = convertLocalsToVariableBindings(locals);
|
||||||
var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast(exp), memo, memo)], variableBindings);
|
var records = [new BindingRecord(ast(exp), memo, new FakeDirectiveMemento(memo, false))];
|
||||||
|
var cd = pcd.instantiate(dispatcher, records, variableBindings, []);
|
||||||
cd.hydrate(context, locals);
|
cd.hydrate(context, locals);
|
||||||
|
|
||||||
return {"changeDetector" : cd, "dispatcher" : dispatcher};
|
return {"changeDetector" : cd, "dispatcher" : dispatcher};
|
||||||
|
@ -56,6 +57,10 @@ export function main() {
|
||||||
return res["dispatcher"].log;
|
return res["dispatcher"].log;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function instantiate(protoChangeDetector, dispatcher, bindings) {
|
||||||
|
return protoChangeDetector.instantiate(dispatcher, bindings, null, []);
|
||||||
|
}
|
||||||
|
|
||||||
describe(`${name} change detection`, () => {
|
describe(`${name} change detection`, () => {
|
||||||
it('should do simple watching', () => {
|
it('should do simple watching', () => {
|
||||||
var person = new Person("misko");
|
var person = new Person("misko");
|
||||||
|
@ -193,7 +198,7 @@ export function main() {
|
||||||
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
var ast = parser.parseInterpolation("B{{a}}A", "location");
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, [new BindingRecord(ast, "memo", "memo")], null);
|
var cd = instantiate(pcd, dispatcher, [new BindingRecord(ast, "memo", null)]);
|
||||||
cd.hydrate(new TestData("value"), null);
|
cd.hydrate(new TestData("value"), null);
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -239,15 +244,18 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("group changes", () => {
|
describe("group changes", () => {
|
||||||
|
var dirMemento1 = new FakeDirectiveMemento(1);
|
||||||
|
var dirMemento2 = new FakeDirectiveMemento(2);
|
||||||
|
|
||||||
it("should notify the dispatcher when a group of records changes", () => {
|
it("should notify the dispatcher when a group of records changes", () => {
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, [
|
var cd = instantiate(pcd, dispatcher, [
|
||||||
new BindingRecord(ast("1 + 2"), "memo", "1"),
|
new BindingRecord(ast("1 + 2"), "memo", dirMemento1),
|
||||||
new BindingRecord(ast("10 + 20"), "memo", "1"),
|
new BindingRecord(ast("10 + 20"), "memo", dirMemento1),
|
||||||
new BindingRecord(ast("100 + 200"), "memo", "2")
|
new BindingRecord(ast("100 + 200"), "memo", dirMemento2)
|
||||||
], null);
|
]);
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
|
||||||
|
@ -257,11 +265,11 @@ export function main() {
|
||||||
it("should notify the dispatcher before switching to the next group", () => {
|
it("should notify the dispatcher before switching to the next group", () => {
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, [
|
var cd = instantiate(pcd, dispatcher, [
|
||||||
new BindingRecord(ast("a()"), "a", "1"),
|
new BindingRecord(ast("a()"), "a", dirMemento1),
|
||||||
new BindingRecord(ast("b()"), "b", "2"),
|
new BindingRecord(ast("b()"), "b", dirMemento2),
|
||||||
new BindingRecord(ast("c()"), "c", "2")
|
new BindingRecord(ast("c()"), "c", dirMemento2)
|
||||||
], null);
|
]);
|
||||||
|
|
||||||
var tr = new TestRecord();
|
var tr = new TestRecord();
|
||||||
tr.a = () => {
|
tr.a = () => {
|
||||||
|
@ -283,6 +291,46 @@ export function main() {
|
||||||
expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]);
|
expect(dispatcher.loggedValues).toEqual(['InvokeA', ['a'], 'InvokeB', 'InvokeC', ['b', 'c']]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("onAllChangesDone", () => {
|
||||||
|
it("should notify the dispatcher about processing all the children", () => {
|
||||||
|
var pcd = createProtoChangeDetector();
|
||||||
|
var dispatcher = new TestDispatcher();
|
||||||
|
|
||||||
|
var memento1 = new FakeDirectiveMemento(1, false);
|
||||||
|
var memento2 = new FakeDirectiveMemento(2, true);
|
||||||
|
|
||||||
|
var cd = pcd.instantiate(dispatcher, [
|
||||||
|
new BindingRecord(ast("1"), "a", memento1),
|
||||||
|
new BindingRecord(ast("2"), "b", memento2)
|
||||||
|
], null, [memento1, memento2]);
|
||||||
|
|
||||||
|
cd.hydrate(null, null);
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.loggedOnAllChangesDone).toEqual([memento2]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should notify in reverse order so the child is always notified before the parent", () => {
|
||||||
|
var pcd = createProtoChangeDetector();
|
||||||
|
var dispatcher = new TestDispatcher();
|
||||||
|
|
||||||
|
var memento1 = new FakeDirectiveMemento(1, true);
|
||||||
|
var memento2 = new FakeDirectiveMemento(2, true);
|
||||||
|
|
||||||
|
var cd = pcd.instantiate(dispatcher, [
|
||||||
|
new BindingRecord(ast("1"), "a", memento1),
|
||||||
|
new BindingRecord(ast("2"), "b", memento2)
|
||||||
|
], null, [memento1, memento2]);
|
||||||
|
|
||||||
|
cd.hydrate(null, null);
|
||||||
|
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
expect(dispatcher.loggedOnAllChangesDone).toEqual([memento2, memento1]);
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("enforce no new changes", () => {
|
describe("enforce no new changes", () => {
|
||||||
|
@ -291,9 +339,9 @@ export function main() {
|
||||||
pcd.addAst(ast("a"), "a", 1);
|
pcd.addAst(ast("a"), "a", 1);
|
||||||
|
|
||||||
var dispatcher = new TestDispatcher();
|
var dispatcher = new TestDispatcher();
|
||||||
var cd = pcd.instantiate(dispatcher, [
|
var cd = instantiate(pcd, dispatcher, [
|
||||||
new BindingRecord(ast("a"), "a", 1)
|
new BindingRecord(ast("a"), "a", 1)
|
||||||
], null);
|
]);
|
||||||
cd.hydrate(new TestData('value'), null);
|
cd.hydrate(new TestData('value'), null);
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
|
@ -308,7 +356,7 @@ export function main() {
|
||||||
var pcd = createProtoChangeDetector();
|
var pcd = createProtoChangeDetector();
|
||||||
var cd = pcd.instantiate(new TestDispatcher(), [
|
var cd = pcd.instantiate(new TestDispatcher(), [
|
||||||
new BindingRecord(ast("invalidProp", "someComponent"), "a", 1)
|
new BindingRecord(ast("invalidProp", "someComponent"), "a", 1)
|
||||||
], null);
|
], null, []);
|
||||||
cd.hydrate(null, null);
|
cd.hydrate(null, null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -363,10 +411,10 @@ export function main() {
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
var protoParent = createProtoChangeDetector();
|
var protoParent = createProtoChangeDetector();
|
||||||
parent = protoParent.instantiate(null, [], null);
|
parent = instantiate(protoParent, null, []);
|
||||||
|
|
||||||
var protoChild = createProtoChangeDetector();
|
var protoChild = createProtoChangeDetector();
|
||||||
child = protoChild.instantiate(null, [], null);
|
child = instantiate(protoChild, null, []);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add children", () => {
|
it("should add children", () => {
|
||||||
|
@ -409,7 +457,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should change CHECK_ONCE to CHECKED", () => {
|
it("should change CHECK_ONCE to CHECKED", () => {
|
||||||
var cd = createProtoChangeDetector().instantiate(null, [], null);
|
var cd = instantiate(createProtoChangeDetector(), null, []);
|
||||||
cd.mode = CHECK_ONCE;
|
cd.mode = CHECK_ONCE;
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -418,7 +466,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should not change the CHECK_ALWAYS", () => {
|
it("should not change the CHECK_ALWAYS", () => {
|
||||||
var cd = createProtoChangeDetector().instantiate(null, [], null);
|
var cd = instantiate(createProtoChangeDetector(), null, []);
|
||||||
cd.mode = CHECK_ALWAYS;
|
cd.mode = CHECK_ALWAYS;
|
||||||
|
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
|
@ -429,7 +477,7 @@ export function main() {
|
||||||
|
|
||||||
describe("markPathToRootAsCheckOnce", () => {
|
describe("markPathToRootAsCheckOnce", () => {
|
||||||
function changeDetector(mode, parent) {
|
function changeDetector(mode, parent) {
|
||||||
var cd = createProtoChangeDetector().instantiate(null, [], null);
|
var cd = instantiate(createProtoChangeDetector(), null, []);
|
||||||
cd.mode = mode;
|
cd.mode = mode;
|
||||||
if (isPresent(parent)) parent.addChild(cd);
|
if (isPresent(parent)) parent.addChild(cd);
|
||||||
return cd;
|
return cd;
|
||||||
|
@ -700,16 +748,28 @@ class TestData {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class FakeDirectiveMemento {
|
||||||
|
value:any;
|
||||||
|
notifyOnAllChangesDone:boolean;
|
||||||
|
|
||||||
|
constructor(value, notifyOnAllChangesDone:boolean = false) {
|
||||||
|
this.value = value;
|
||||||
|
this.notifyOnAllChangesDone = notifyOnAllChangesDone;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class TestDispatcher extends ChangeDispatcher {
|
class TestDispatcher extends ChangeDispatcher {
|
||||||
log:List;
|
log:List;
|
||||||
loggedValues:List;
|
loggedValues:List;
|
||||||
changeRecords:List;
|
changeRecords:List;
|
||||||
|
loggedOnAllChangesDone:List;
|
||||||
onChange:Function;
|
onChange:Function;
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
super();
|
super();
|
||||||
this.log = null;
|
this.log = null;
|
||||||
this.loggedValues = null;
|
this.loggedValues = null;
|
||||||
|
this.loggedOnAllChangesDone = null;
|
||||||
this.onChange = (_, __) => {};
|
this.onChange = (_, __) => {};
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
|
@ -717,6 +777,7 @@ class TestDispatcher extends ChangeDispatcher {
|
||||||
clear() {
|
clear() {
|
||||||
this.log = ListWrapper.create();
|
this.log = ListWrapper.create();
|
||||||
this.loggedValues = ListWrapper.create();
|
this.loggedValues = ListWrapper.create();
|
||||||
|
this.loggedOnAllChangesDone = ListWrapper.create();
|
||||||
this.changeRecords = ListWrapper.create();
|
this.changeRecords = ListWrapper.create();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -724,7 +785,7 @@ class TestDispatcher extends ChangeDispatcher {
|
||||||
ListWrapper.push(this.loggedValues, value);
|
ListWrapper.push(this.loggedValues, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
onRecordChange(group, changeRecords:List) {
|
onRecordChange(directiveMemento, changeRecords:List) {
|
||||||
var value = changeRecords[0].change.currentValue;
|
var value = changeRecords[0].change.currentValue;
|
||||||
var memento = changeRecords[0].bindingMemento;
|
var memento = changeRecords[0].bindingMemento;
|
||||||
ListWrapper.push(this.log, memento + '=' + this._asString(value));
|
ListWrapper.push(this.log, memento + '=' + this._asString(value));
|
||||||
|
@ -734,9 +795,12 @@ class TestDispatcher extends ChangeDispatcher {
|
||||||
|
|
||||||
ListWrapper.push(this.changeRecords, changeRecords);
|
ListWrapper.push(this.changeRecords, changeRecords);
|
||||||
|
|
||||||
this.onChange(group, changeRecords);
|
this.onChange(directiveMemento, changeRecords);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onAllChangesDone(directiveMemento) {
|
||||||
|
ListWrapper.push(this.loggedOnAllChangesDone, directiveMemento);
|
||||||
|
}
|
||||||
|
|
||||||
_asString(value) {
|
_asString(value) {
|
||||||
return (isBlank(value) ? 'null' : value.toString());
|
return (isBlank(value) ? 'null' : value.toString());
|
||||||
|
|
|
@ -222,6 +222,18 @@ export function main() {
|
||||||
|
|
||||||
expect(protoChild.directParent()).toEqual(null);
|
expect(protoChild.directParent()).toEqual(null);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("should allow for direct access using getDirectiveBindingAtIndex", function () {
|
||||||
|
var binding = DirectiveBinding.createFromBinding(
|
||||||
|
bind(SimpleDirective).toClass(SimpleDirective), null);
|
||||||
|
var proto = new ProtoElementInjector(null, 0, [binding]);
|
||||||
|
|
||||||
|
expect(proto.getDirectiveBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding);
|
||||||
|
expect(() => proto.getDirectiveBindingAtIndex(-1)).toThrowError(
|
||||||
|
'Index -1 is out-of-bounds.');
|
||||||
|
expect(() => proto.getDirectiveBindingAtIndex(10)).toThrowError(
|
||||||
|
'Index 10 is out-of-bounds.');
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -419,17 +431,6 @@ export function main() {
|
||||||
'Index 10 is out-of-bounds.');
|
'Index 10 is out-of-bounds.');
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should allow for direct access using getBindingAtIndex", function () {
|
|
||||||
var inj = injector([
|
|
||||||
DirectiveBinding.createFromBinding(bind(SimpleDirective).toClass(SimpleDirective), null)
|
|
||||||
]);
|
|
||||||
expect(inj.getDirectiveBindingAtIndex(0)).toBeAnInstanceOf(DirectiveBinding);
|
|
||||||
expect(() => inj.getDirectiveBindingAtIndex(-1)).toThrowError(
|
|
||||||
'Index -1 is out-of-bounds.');
|
|
||||||
expect(() => inj.getDirectiveBindingAtIndex(10)).toThrowError(
|
|
||||||
'Index 10 is out-of-bounds.');
|
|
||||||
});
|
|
||||||
|
|
||||||
it("should handle cyclic dependencies", function () {
|
it("should handle cyclic dependencies", function () {
|
||||||
expect(() => {
|
expect(() => {
|
||||||
var bAneedsB = bind(A_Needs_B).toFactory((a) => new A_Needs_B(a), [B_Needs_A]);
|
var bAneedsB = bind(A_Needs_B).toFactory((a) => new A_Needs_B(a), [B_Needs_A]);
|
||||||
|
|
|
@ -11,7 +11,7 @@ import {DynamicProtoChangeDetector, ChangeDetector, Lexer, Parser} from 'angular
|
||||||
|
|
||||||
function createView(nodes) {
|
function createView(nodes) {
|
||||||
var view = new View(null, nodes, MapWrapper.create());
|
var view = new View(null, nodes, MapWrapper.create());
|
||||||
var cd = new DynamicProtoChangeDetector(null).instantiate(view, [], null);
|
var cd = new DynamicProtoChangeDetector(null).instantiate(view, [], null, []);
|
||||||
view.init(cd, [], [], [], [], [], [], [], [], []);
|
view.init(cd, [], [], [], [], [], [], [], [], []);
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'angul
|
||||||
import {ProtoElementInjector, ElementInjector, DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
import {ProtoElementInjector, ElementInjector, DirectiveBinding} from 'angular2/src/core/compiler/element_injector';
|
||||||
import {EmulatedScopedShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
import {EmulatedScopedShadowDomStrategy, NativeShadowDomStrategy} from 'angular2/src/core/compiler/shadow_dom_strategy';
|
||||||
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
import {DirectiveMetadataReader} from 'angular2/src/core/compiler/directive_metadata_reader';
|
||||||
import {Component, Decorator, Viewport, Directive, onChange} from 'angular2/src/core/annotations/annotations';
|
import {Component, Decorator, Viewport, Directive, onChange, onAllChangesDone} from 'angular2/src/core/annotations/annotations';
|
||||||
import {Lexer, Parser, DynamicProtoChangeDetector,
|
import {Lexer, Parser, DynamicProtoChangeDetector,
|
||||||
ChangeDetector} from 'angular2/change_detection';
|
ChangeDetector} from 'angular2/change_detection';
|
||||||
import {EventEmitter} from 'angular2/src/core/annotations/di';
|
import {EventEmitter} from 'angular2/src/core/annotations/di';
|
||||||
|
@ -627,6 +627,21 @@ export function main() {
|
||||||
cd.detectChanges();
|
cd.detectChanges();
|
||||||
expect(directive.changes).toEqual({"a" : new PropertyUpdate(100, 0)});
|
expect(directive.changes).toEqual({"a" : new PropertyUpdate(100, 0)});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should invoke the onAllChangesDone callback', () => {
|
||||||
|
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
|
||||||
|
new DynamicProtoChangeDetector(null), null);
|
||||||
|
|
||||||
|
pv.bindElement(null, 0, new ProtoElementInjector(null, 0, [
|
||||||
|
DirectiveBinding.createFromType(DirectiveImplementingOnAllChangesDone, new Directive({lifecycle: [onAllChangesDone]}))
|
||||||
|
]));
|
||||||
|
|
||||||
|
createViewAndChangeDetector(pv);
|
||||||
|
cd.detectChanges();
|
||||||
|
|
||||||
|
var directive = view.elementInjectors[0].get(DirectiveImplementingOnAllChangesDone);
|
||||||
|
expect(directive.onAllChangesDoneCalled).toBe(true);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -678,6 +693,14 @@ class DirectiveImplementingOnChange {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class DirectiveImplementingOnAllChangesDone {
|
||||||
|
onAllChangesDoneCalled;
|
||||||
|
|
||||||
|
onAllChangesDone() {
|
||||||
|
this.onAllChangesDoneCalled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class SomeService {}
|
class SomeService {}
|
||||||
|
|
||||||
@Component({services: [SomeService]})
|
@Component({services: [SomeService]})
|
||||||
|
|
|
@ -105,7 +105,7 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) {
|
||||||
var parser = new Parser(new Lexer());
|
var parser = new Parser(new Lexer());
|
||||||
|
|
||||||
var parentProto = changeDetection.createProtoChangeDetector('parent');
|
var parentProto = changeDetection.createProtoChangeDetector('parent');
|
||||||
var parentCd = parentProto.instantiate(dispatcher, [], []);
|
var parentCd = parentProto.instantiate(dispatcher, [], [], []);
|
||||||
|
|
||||||
var proto = changeDetection.createProtoChangeDetector("proto");
|
var proto = changeDetection.createProtoChangeDetector("proto");
|
||||||
var bindingRecords = [
|
var bindingRecords = [
|
||||||
|
@ -126,7 +126,7 @@ function setUpChangeDetection(changeDetection:ChangeDetection, iterations) {
|
||||||
for (var j = 0; j < 10; ++j) {
|
for (var j = 0; j < 10; ++j) {
|
||||||
obj.setField(j, i);
|
obj.setField(j, i);
|
||||||
}
|
}
|
||||||
var cd = proto.instantiate(dispatcher, bindingRecords, []);
|
var cd = proto.instantiate(dispatcher, bindingRecords, [], []);
|
||||||
cd.hydrate(obj, null);
|
cd.hydrate(obj, null);
|
||||||
parentCd.addChild(cd);
|
parentCd.addChild(cd);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue