feat(change_detection): reimplement change detection

This commit is contained in:
vsavkin
2015-01-14 13:51:16 -08:00
parent 22653707d9
commit 9957c1338e
30 changed files with 1257 additions and 2233 deletions

View File

@ -4,7 +4,7 @@ import {DOM, Element} from 'facade/dom';
import {Compiler, CompilerCache} from './compiler/compiler';
import {ProtoView} from './compiler/view';
import {Reflector, reflector} from 'reflection/reflection';
import {Parser, Lexer, ChangeDetector, RecordRange} from 'change_detection/change_detection';
import {Parser, Lexer, ChangeDetector} from 'change_detection/change_detection';
import {TemplateLoader} from './compiler/template_loader';
import {DirectiveMetadataReader} from './compiler/directive_metadata_reader';
import {DirectiveMetadata} from './compiler/directive_metadata';
@ -21,7 +21,7 @@ var _rootBindings = [
];
export var appViewToken = new OpaqueToken('AppView');
export var appRecordRangeToken = new OpaqueToken('AppRecordRange');
export var appChangeDetectorToken = new OpaqueToken('AppChangeDetector');
export var appElementToken = new OpaqueToken('AppElement');
export var appComponentAnnotatedTypeToken = new OpaqueToken('AppComponentAnnotatedType');
export var appDocumentToken = new OpaqueToken('AppDocument');
@ -49,24 +49,22 @@ function _injectorBindings(appComponentType) {
appComponentAnnotatedType) => {
return compiler.compile(appComponentAnnotatedType.type, null).then(
(protoView) => {
var appProtoView = ProtoView.createRootProtoView(protoView,
var appProtoView = ProtoView.createRootProtoView(protoView,
appElement, appComponentAnnotatedType);
// The light Dom of the app element is not considered part of
// the angular application. Thus the context and lightDomInjector are
// empty.
var view = appProtoView.instantiate(null);
view.hydrate(injector, null, new Object());
var view = appProtoView.instantiate(null);
view.hydrate(injector, null, new Object());
return view;
});
}, [Compiler, Injector, appElementToken, appComponentAnnotatedTypeToken]),
bind(appRecordRangeToken).toFactory((rootView) => rootView.recordRange,
bind(appChangeDetectorToken).toFactory((rootView) => rootView.changeDetector,
[appViewToken]),
bind(ChangeDetector).toFactory((appRecordRange) =>
new ChangeDetector(appRecordRange, assertionsEnabled()), [appRecordRangeToken]),
bind(appComponentType).toFactory((rootView) => rootView.elementInjectors[0].getComponent(),
[appViewToken]),
bind(LifeCycle).toClass(LifeCycle)
bind(LifeCycle).toFactory((cd) => new LifeCycle(cd, assertionsEnabled()), [appChangeDetectorToken])
];
}

View File

@ -9,7 +9,7 @@ import {ElementBinder} from '../element_binder';
import {ProtoElementInjector} from '../element_injector';
import {ProtoView} from '../view';
import {ASTWithSource} from 'change_detection/change_detection';
import {AST} from 'change_detection/change_detection';
/**
* Collects all data that is needed to process an element
@ -86,14 +86,14 @@ export class CompileElement {
return this._classList;
}
addTextNodeBinding(indexInParent:int, expression:ASTWithSource) {
addTextNodeBinding(indexInParent:int, expression:AST) {
if (isBlank(this.textNodeBindings)) {
this.textNodeBindings = MapWrapper.create();
}
MapWrapper.set(this.textNodeBindings, indexInParent, expression);
}
addPropertyBinding(property:string, expression:ASTWithSource) {
addPropertyBinding(property:string, expression:AST) {
if (isBlank(this.propertyBindings)) {
this.propertyBindings = MapWrapper.create();
}
@ -107,7 +107,7 @@ export class CompileElement {
MapWrapper.set(this.variableBindings, contextName, templateName);
}
addEventBinding(eventName:string, expression:ASTWithSource) {
addEventBinding(eventName:string, expression:AST) {
if (isBlank(this.eventBindings)) {
this.eventBindings = MapWrapper.create();
}

View File

@ -4,7 +4,7 @@ import {ListWrapper, List, MapWrapper, StringMapWrapper} from 'facade/collection
import {reflector} from 'reflection/reflection';
import {Parser, ProtoRecordRange} from 'change_detection/change_detection';
import {Parser, ProtoChangeDetector} from 'change_detection/change_detection';
import {Component, Directive} from '../../annotations/annotations';
import {DirectiveMetadata} from '../directive_metadata';
@ -18,7 +18,7 @@ import {CompileControl} from './compile_control';
/**
* Creates the ElementBinders and adds watches to the
* ProtoRecordRange.
* ProtoChangeDetector.
*
* Fills:
* - CompileElement#inheritedElementBinder

View File

@ -2,7 +2,7 @@ import {isPresent, BaseException} from 'facade/lang';
import {ListWrapper, MapWrapper} from 'facade/collection';
import {ProtoView} from '../view';
import {ProtoRecordRange} from 'change_detection/change_detection';
import {ProtoChangeDetector} from 'change_detection/change_detection';
import {CompileStep} from './compile_step';
import {CompileElement} from './compile_element';
@ -21,7 +21,7 @@ export class ProtoViewBuilder extends CompileStep {
process(parent:CompileElement, current:CompileElement, control:CompileControl) {
var inheritedProtoView = null;
if (current.isViewRoot) {
inheritedProtoView = new ProtoView(current.element, new ProtoRecordRange());
inheritedProtoView = new ProtoView(current.element, new ProtoChangeDetector());
if (isPresent(parent)) {
if (isPresent(parent.inheritedElementBinder.nestedProtoView)) {
throw new BaseException('Only one nested view per element is allowed');

View File

@ -1,7 +1,7 @@
import {DOM, Element, Node, Text, DocumentFragment, TemplateElement} from 'facade/dom';
import {ListWrapper, MapWrapper, StringMapWrapper, List} from 'facade/collection';
import {ProtoRecordRange, RecordRange, Record,
ChangeDispatcher, AST, ContextWithVariableBindings} from 'change_detection/change_detection';
import {AST, ContextWithVariableBindings, ChangeDispatcher, ProtoChangeDetector, ChangeDetector, ChangeRecord}
from 'change_detection/change_detection';
import {ProtoElementInjector, ElementInjector, PreBuiltObjects} from './element_injector';
import {ElementBinder} from './element_binder';
@ -30,7 +30,7 @@ export class View {
elementInjectors:List<ElementInjector>;
bindElements:List<Element>;
textNodes:List<Text>;
recordRange:RecordRange;
changeDetector:ChangeDetector;
/// When the view is part of render tree, the DocumentFragment is empty, which is why we need
/// to keep track of the nodes.
nodes:List<Node>;
@ -41,10 +41,10 @@ export class View {
context: any;
contextWithLocals:ContextWithVariableBindings;
constructor(proto:ProtoView, nodes:List<Node>, protoRecordRange:ProtoRecordRange, protoContextLocals:Map) {
constructor(proto:ProtoView, nodes:List<Node>, protoChangeDetector:ProtoChangeDetector, protoContextLocals:Map) {
this.proto = proto;
this.nodes = nodes;
this.recordRange = protoRecordRange.instantiate(this, NO_FORMATTERS);
this.changeDetector = protoChangeDetector.instantiate(this, NO_FORMATTERS);
this.elementInjectors = null;
this.rootElementInjectors = null;
this.textNodes = null;
@ -92,7 +92,7 @@ export class View {
// TODO(tbosch): if we have a contextWithLocals we actually only need to
// set the contextWithLocals once. Would it be faster to always use a contextWithLocals
// even if we don't have locals and not update the recordRange here?
this.recordRange.setContext(this.context);
this.changeDetector.setContext(this.context);
}
_dehydrateContext() {
@ -195,20 +195,20 @@ export class View {
this._dehydrateContext();
}
onRecordChange(groupMemento, records:List<Record>) {
this._invokeMementoForRecords(records);
onRecordChange(groupMemento, records:List) {
this._invokeMementos(records);
if (groupMemento instanceof DirectivePropertyGroupMemento) {
this._notifyDirectiveAboutChanges(groupMemento, records);
}
}
_invokeMementoForRecords(records:List<Record>) {
_invokeMementos(records:List) {
for(var i = 0; i < records.length; ++i) {
this._invokeMementoFor(records[i]);
}
}
_notifyDirectiveAboutChanges(groupMemento, records:List<Record>) {
_notifyDirectiveAboutChanges(groupMemento, records:List) {
var dir = groupMemento.directive(this.elementInjectors);
if (dir instanceof OnChange) {
dir.onChange(this._collectChanges(records));
@ -216,8 +216,8 @@ export class View {
}
// dispatch to element injector or text nodes based on context
_invokeMementoFor(record:Record) {
var memento = record.expressionMemento();
_invokeMementoFor(record:ChangeRecord) {
var memento = record.bindingMemento;
if (memento instanceof DirectivePropertyMemento) {
// we know that it is DirectivePropertyMemento
var directiveMemento:DirectivePropertyMemento = memento;
@ -234,12 +234,12 @@ export class View {
}
}
_collectChanges(records:List<Record>) {
_collectChanges(records:List) {
var changes = StringMapWrapper.create();
for(var i = 0; i < records.length; ++i) {
var record = records[i];
var propertyUpdate = new PropertyUpdate(record.currentValue, record.previousValue);
StringMapWrapper.set(changes, record.expressionMemento()._setterName, propertyUpdate);
StringMapWrapper.set(changes, record.bindingMemento._setterName, propertyUpdate);
}
return changes;
}
@ -248,7 +248,7 @@ export class View {
export class ProtoView {
element:Element;
elementBinders:List<ElementBinder>;
protoRecordRange:ProtoRecordRange;
protoChangeDetector:ProtoChangeDetector;
variableBindings: Map;
protoContextLocals:Map;
textNodesWithBindingCount:int;
@ -258,12 +258,12 @@ export class ProtoView {
isTemplateElement:boolean;
constructor(
template:Element,
protoRecordRange:ProtoRecordRange) {
protoChangeDetector:ProtoChangeDetector) {
this.element = template;
this.elementBinders = [];
this.variableBindings = MapWrapper.create();
this.protoContextLocals = MapWrapper.create();
this.protoRecordRange = protoRecordRange;
this.protoChangeDetector = protoChangeDetector;
this.textNodesWithBindingCount = 0;
this.elementsWithBindingCount = 0;
this.instantiateInPlace = false;
@ -299,8 +299,8 @@ export class ProtoView {
} else {
viewNodes = [rootElementClone];
}
var view = new View(this, viewNodes, this.protoRecordRange, this.protoContextLocals);
var view = new View(this, viewNodes, this.protoChangeDetector, this.protoContextLocals);
var binders = this.elementBinders;
var elementInjectors = ListWrapper.createFixedSize(binders.length);
var rootElementInjectors = [];
@ -353,7 +353,7 @@ export class ProtoView {
var lightDom = null;
if (isPresent(binder.componentDirective)) {
var childView = binder.nestedProtoView.instantiate(elementInjector);
view.recordRange.addRange(childView.recordRange);
view.changeDetector.addChild(childView.changeDetector);
lightDom = binder.componentDirective.shadowDomStrategy.constructLightDom(view, childView, element);
binder.componentDirective.shadowDomStrategy.attachTemplate(element, childView);
@ -434,7 +434,7 @@ export class ProtoView {
}
ListWrapper.push(elBinder.textNodeIndices, indexInParent);
var memento = this.textNodesWithBindingCount++;
this.protoRecordRange.addRecordsFromAST(expression, memento, memento);
this.protoChangeDetector.addAst(expression, memento, memento);
}
/**
@ -447,7 +447,7 @@ export class ProtoView {
this.elementsWithBindingCount++;
}
var memento = new ElementPropertyMemento(this.elementsWithBindingCount-1, setterName, setter);
this.protoRecordRange.addRecordsFromAST(expression, memento, memento);
this.protoChangeDetector.addAst(expression, memento, memento);
}
/**
@ -478,7 +478,7 @@ export class ProtoView {
setter
);
var groupMemento = DirectivePropertyGroupMemento.get(expMemento);
this.protoRecordRange.addRecordsFromAST(expression, expMemento, groupMemento, isContentWatch);
this.protoChangeDetector.addAst(expression, expMemento, groupMemento, isContentWatch);
}
// Create a rootView as if the compiler encountered <rootcmp></rootcmp>,
@ -487,7 +487,7 @@ export class ProtoView {
static createRootProtoView(protoView: ProtoView,
insertionElement, rootComponentAnnotatedType: DirectiveMetadata): ProtoView {
DOM.addClass(insertionElement, 'ng-binding');
var rootProtoView = new ProtoView(insertionElement, new ProtoRecordRange());
var rootProtoView = new ProtoView(insertionElement, new ProtoChangeDetector());
rootProtoView.instantiateInPlace = true;
var binder = rootProtoView.bindElement(
new ProtoElementInjector(null, 0, [rootComponentAnnotatedType.type], true));
@ -507,7 +507,7 @@ export class ElementPropertyMemento {
this._setter = setter;
}
invoke(record:Record, bindElements:List<Element>) {
invoke(record:ChangeRecord, bindElements:List<Element>) {
var element:Element = bindElements[this._elementIndex];
this._setter(element, record.currentValue);
}
@ -529,7 +529,7 @@ export class DirectivePropertyMemento {
this._setter = setter;
}
invoke(record:Record, elementInjectors:List<ElementInjector>) {
invoke(record:ChangeRecord, elementInjectors:List<ElementInjector>) {
var elementInjector:ElementInjector = elementInjectors[this._elementInjectorIndex];
var directive = elementInjector.getAtIndex(this._directiveIndex);
this._setter(directive, record.currentValue);

View File

@ -84,7 +84,7 @@ export class ViewPort {
} else {
this._lightDom.redistribute();
}
this.parentView.recordRange.addRange(view.recordRange);
this.parentView.changeDetector.addChild(view.changeDetector);
this._linkElementInjectors(view);
return view;
}
@ -98,7 +98,7 @@ export class ViewPort {
} else {
this._lightDom.redistribute();
}
removedView.recordRange.remove();
removedView.changeDetector.remove();
this._unlinkElementInjectors(removedView);
return removedView;
}

View File

@ -5,9 +5,11 @@ import {ListWrapper} from 'facade/collection';
export class LifeCycle {
_changeDetector:ChangeDetector;
_enforceNoNewChanges:boolean;
constructor(changeDetector:ChangeDetector) {
constructor(changeDetector:ChangeDetector, enforceNoNewChanges:boolean = false) {
this._changeDetector = changeDetector;
this._enforceNoNewChanges = enforceNoNewChanges;
}
registerWith(zone:VmTurnZone) {
@ -26,5 +28,8 @@ export class LifeCycle {
tick() {
this._changeDetector.detectChanges();
if (this._enforceNoNewChanges) {
this._changeDetector.checkNoChanges();
}
}
}

View File

@ -29,7 +29,7 @@ export function main() {
ctx = new MyComp();
view = pv.instantiate(null);
view.hydrate(new Injector([]), null, ctx);
cd = new ChangeDetector(view.recordRange);
cd = view.changeDetector;
}
it('should consume text node changes', (done) => {

View File

@ -16,7 +16,7 @@ import {ProtoView, ElementPropertyMemento, DirectivePropertyMemento} from 'core/
import {ProtoElementInjector} from 'core/compiler/element_injector';
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
import {ChangeDetector, Lexer, Parser, ProtoRecordRange} from 'change_detection/change_detection';
import {ChangeDetector, Lexer, Parser, ProtoChangeDetector} from 'change_detection/change_detection';
import {Injector} from 'di/di';
export function main() {
@ -65,7 +65,7 @@ export function main() {
}
if (isPresent(current.element.getAttribute('viewroot'))) {
current.isViewRoot = true;
current.inheritedProtoView = new ProtoView(current.element, new ProtoRecordRange());
current.inheritedProtoView = new ProtoView(current.element, new ProtoChangeDetector());
} else if (isPresent(parent)) {
current.inheritedProtoView = parent.inheritedProtoView;
}
@ -77,7 +77,7 @@ export function main() {
evalContext = new Context();
view = protoView.instantiate(null);
view.hydrate(new Injector([]), null, evalContext);
changeDetector = new ChangeDetector(view.recordRange);
changeDetector = view.changeDetector;
}
it('should not create an ElementBinder for elements that have no bindings', () => {
@ -204,7 +204,7 @@ export function main() {
var results = pipeline.process(el('<div viewroot prop-binding directives></div>'));
var pv = results[0].inheritedProtoView;
results[0].inheritedElementBinder.nestedProtoView = new ProtoView(
el('<div></div>'), new ProtoRecordRange());
el('<div></div>'), new ProtoChangeDetector());
instantiateView(pv);
evalContext.prop1 = 'a';

View File

@ -34,7 +34,7 @@ export function main() {
compiler.compile(MyComp, el(template)).
then(createView).
then((view) => {
var lc = new LifeCycle(new ChangeDetector(view.recordRange));
var lc = new LifeCycle(view.changeDetector, false);
assertions(view, lc);
});
}

View File

@ -5,7 +5,7 @@ import {ShadowDomEmulated} from 'core/compiler/shadow_dom';
import {DirectiveMetadataReader} from 'core/compiler/directive_metadata_reader';
import {Component, Decorator, Template} from 'core/annotations/annotations';
import {OnChange} from 'core/core';
import {Lexer, Parser, ProtoRecordRange, ChangeDetector} from 'change_detection/change_detection';
import {Lexer, Parser, ProtoChangeDetector, ChangeDetector} from 'change_detection/change_detection';
import {TemplateConfig} from 'core/annotations/template_config';
import {List, MapWrapper} from 'facade/collection';
import {DOM, Element} from 'facade/dom';
@ -48,10 +48,10 @@ export function main() {
someTemplateDirective = new DirectiveMetadataReader().read(SomeTemplate);
});
describe('instatiated from protoView', () => {
describe('instantiated from protoView', () => {
var view;
beforeEach(() => {
var pv = new ProtoView(el('<div id="1"></div>'), new ProtoRecordRange());
var pv = new ProtoView(el('<div id="1"></div>'), new ProtoChangeDetector());
view = pv.instantiate(null);
});
@ -72,7 +72,7 @@ export function main() {
describe('with locals', function() {
var view;
beforeEach(() => {
var pv = new ProtoView(el('<div id="1"></div>'), new ProtoRecordRange());
var pv = new ProtoView(el('<div id="1"></div>'), new ProtoChangeDetector());
pv.bindVariable('context-foo', 'template-foo');
view = createView(pv);
});
@ -108,7 +108,7 @@ export function main() {
}
it('should collect the root node in the ProtoView element', () => {
var pv = new ProtoView(templateAwareCreateElement('<div id="1"></div>'), new ProtoRecordRange());
var pv = new ProtoView(templateAwareCreateElement('<div id="1"></div>'), new ProtoChangeDetector());
var view = pv.instantiate(null);
view.hydrate(null, null, null);
expect(view.nodes.length).toBe(1);
@ -118,7 +118,7 @@ export function main() {
describe('collect elements with property bindings', () => {
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>'), new ProtoRecordRange());
var pv = new ProtoView(templateAwareCreateElement('<div [prop]="a" class="ng-binding"></div>'), new ProtoChangeDetector());
pv.bindElement(null);
pv.bindElementProperty(parser.parseBinding('a', null), 'prop', reflector.setter('prop'));
@ -130,7 +130,7 @@ export function main() {
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>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(null);
pv.bindElementProperty(parser.parseBinding('b', null), 'a', reflector.setter('a'));
@ -145,7 +145,7 @@ export function main() {
describe('collect text nodes with bindings', () => {
it('should collect text nodes under the root element', () => {
var pv = new ProtoView(templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'), new ProtoRecordRange());
var pv = new ProtoView(templateAwareCreateElement('<div class="ng-binding">{{}}<span></span>{{}}</div>'), new ProtoChangeDetector());
pv.bindElement(null);
pv.bindTextNode(0, parser.parseBinding('a', null));
pv.bindTextNode(2, parser.parseBinding('b', null));
@ -159,7 +159,7 @@ export function main() {
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>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(null);
pv.bindTextNode(0, parser.parseBinding('b', null));
@ -175,7 +175,7 @@ export function main() {
describe('inplace instantiation', () => {
it('should be supported.', () => {
var template = el('<div></div>');
var pv = new ProtoView(template, new ProtoRecordRange());
var pv = new ProtoView(template, new ProtoChangeDetector());
pv.instantiateInPlace = true;
var view = pv.instantiate(null);
view.hydrate(null, null, null);
@ -184,8 +184,8 @@ export function main() {
it('should be off by default.', () => {
var template = el('<div></div>')
var view = new ProtoView(template, new ProtoRecordRange())
.instantiate(null);
var view = new ProtoView(template, new ProtoChangeDetector())
.instantiate(null);
view.hydrate(null, null, null);
expect(view.nodes[0]).not.toBe(template);
});
@ -201,7 +201,7 @@ export function main() {
describe('create ElementInjectors', () => {
it('should use the directives of the ProtoElementInjector', () => {
var pv = new ProtoView(el('<div class="ng-binding"></div>'), new ProtoRecordRange());
var pv = new ProtoView(el('<div class="ng-binding"></div>'), new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
var view = pv.instantiate(null);
@ -212,7 +212,7 @@ export function main() {
it('should use the correct parent', () => {
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
pv.bindElement(protoParent);
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
@ -226,7 +226,7 @@ export function main() {
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>'),
new ProtoRecordRange());
new ProtoChangeDetector());
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
pv.bindElement(protoParent);
var testProtoElementInjector = new TestProtoElementInjector(protoParent, 1, [AnotherDirective]);
@ -242,7 +242,7 @@ export function main() {
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>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
var testProtoElementInjector = new TestProtoElementInjector(null, 1, [AnotherDirective]);
pv.bindElement(testProtoElementInjector);
@ -259,7 +259,7 @@ export function main() {
it('should collect a single root element injector', () => {
var pv = new ProtoView(el('<div class="ng-binding"><span class="ng-binding"></span></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
var protoParent = new ProtoElementInjector(null, 0, [SomeDirective]);
pv.bindElement(protoParent);
pv.bindElement(new ProtoElementInjector(protoParent, 1, [AnotherDirective]));
@ -272,7 +272,7 @@ export function main() {
it('should collect multiple root element injectors', () => {
var pv = new ProtoView(el('<div><span class="ng-binding"></span><span class="ng-binding"></span></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
pv.bindElement(new ProtoElementInjector(null, 2, [AnotherDirective]));
@ -289,7 +289,7 @@ export function main() {
var ctx;
function createComponentWithSubPV(subProtoView) {
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'), new ProtoRecordRange());
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'), new ProtoChangeDetector());
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponent], true));
binder.componentDirective = someComponentDirective;
binder.nestedProtoView = subProtoView;
@ -304,7 +304,7 @@ export function main() {
}
it('should expose component services to the component', () => {
var subpv = new ProtoView(el('<span></span>'), new ProtoRecordRange());
var subpv = new ProtoView(el('<span></span>'), new ProtoChangeDetector());
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
@ -316,7 +316,7 @@ export function main() {
it('should expose component services and component instance to directives in the shadow Dom',
() => {
var subpv = new ProtoView(
el('<div dec class="ng-binding">hello shadow dom</div>'), new ProtoRecordRange());
el('<div dec class="ng-binding">hello shadow dom</div>'), new ProtoChangeDetector());
subpv.bindElement(
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
var pv = createComponentWithSubPV(subpv);
@ -339,7 +339,7 @@ export function main() {
it('dehydration should dehydrate child component views too', () => {
var subpv = new ProtoView(
el('<div dec class="ng-binding">hello shadow dom</div>'), new ProtoRecordRange());
el('<div dec class="ng-binding">hello shadow dom</div>'), new ProtoChangeDetector());
subpv.bindElement(
new ProtoElementInjector(null, 0, [ServiceDependentDecorator]));
var pv = createComponentWithSubPV(subpv);
@ -354,7 +354,7 @@ export function main() {
});
it('should create shadow dom', () => {
var subpv = new ProtoView(el('<span>hello shadow dom</span>'), new ProtoRecordRange());
var subpv = new ProtoView(el('<span>hello shadow dom</span>'), new ProtoChangeDetector());
var pv = createComponentWithSubPV(subpv);
var view = createNestedView(pv);
@ -363,9 +363,9 @@ export function main() {
});
it('should use the provided shadow DOM strategy', () => {
var subpv = new ProtoView(el('<span>hello shadow dom</span>'), new ProtoRecordRange());
var subpv = new ProtoView(el('<span>hello shadow dom</span>'), new ProtoChangeDetector());
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'), new ProtoRecordRange());
var pv = new ProtoView(el('<cmp class="ng-binding"></cmp>'), new ProtoChangeDetector());
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeComponentWithEmulatedShadowDom], true));
binder.componentDirective = new DirectiveMetadataReader().read(SomeComponentWithEmulatedShadowDom);
binder.nestedProtoView = subpv;
@ -379,8 +379,8 @@ export function main() {
describe('with template views', () => {
function createViewWithTemplate() {
var templateProtoView = new ProtoView(
el('<div id="1"></div>'), new ProtoRecordRange());
var pv = new ProtoView(el('<someTmpl class="ng-binding"></someTmpl>'), new ProtoRecordRange());
el('<div id="1"></div>'), new ProtoChangeDetector());
var pv = new ProtoView(el('<someTmpl class="ng-binding"></someTmpl>'), new ProtoChangeDetector());
var binder = pv.bindElement(new ProtoElementInjector(null, 0, [SomeTemplate]));
binder.templateDirective = someTemplateDirective;
binder.nestedProtoView = templateProtoView;
@ -424,7 +424,7 @@ export function main() {
function createProtoView() {
var pv = new ProtoView(el('<div class="ng-binding"><div></div></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new TestProtoElementInjector(null, 0, []));
pv.bindEvent('click', parser.parseBinding('callMe(\$event)', null));
return pv;
@ -464,12 +464,12 @@ export function main() {
function createViewAndChangeDetector(protoView) {
view = createView(protoView);
ctx = view.context;
cd = new ChangeDetector(view.recordRange);
cd = view.changeDetector;
}
it('should consume text node changes', () => {
var pv = new ProtoView(el('<div class="ng-binding">{{}}</div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(null);
pv.bindTextNode(0, parser.parseBinding('foo', null));
createViewAndChangeDetector(pv);
@ -481,7 +481,7 @@ export function main() {
it('should consume element binding changes', () => {
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(null);
pv.bindElementProperty(parser.parseBinding('foo', null), 'id', reflector.setter('id'));
createViewAndChangeDetector(pv);
@ -493,7 +493,7 @@ export function main() {
it('should consume directive watch expression change', () => {
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 0, [SomeDirective]));
pv.bindDirectiveProperty(0, parser.parseBinding('foo', null), 'prop', reflector.setter('prop'), false);
createViewAndChangeDetector(pv);
@ -505,7 +505,7 @@ export function main() {
it('should notify a directive about changes after all its properties have been set', () => {
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'), false);
@ -522,7 +522,7 @@ export function main() {
it('should provide a map of updated properties', () => {
var pv = new ProtoView(el('<div class="ng-binding"></div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 0, [DirectiveImplementingOnChange]));
pv.bindDirectiveProperty( 0, parser.parseBinding('a', null), 'a', reflector.setter('a'), false);
@ -547,7 +547,7 @@ export function main() {
var element, pv;
beforeEach(() => {
element = DOM.createElement('div');
pv = new ProtoView(el('<div>hi</div>'), new ProtoRecordRange());
pv = new ProtoView(el('<div>hi</div>'), new ProtoChangeDetector());
});
it('should create the root component when instantiated', () => {

View File

@ -5,10 +5,10 @@ import {DOM} from 'facade/dom';
import {ListWrapper, MapWrapper} from 'facade/collection';
import {Injector} from 'di/di';
import {ProtoElementInjector, ElementInjector} from 'core/compiler/element_injector';
import {ProtoRecordRange, Lexer, Parser} from 'change_detection/change_detection';
import {ProtoChangeDetector, Lexer, Parser} from 'change_detection/change_detection';
function createView(nodes) {
var view = new View(null, nodes, new ProtoRecordRange(), MapWrapper.create());
var view = new View(null, nodes, new ProtoChangeDetector(), MapWrapper.create());
view.init([], [], [], [], [], [], []);
return view;
}
@ -22,7 +22,7 @@ export function main() {
dom = el(`<div><stuff></stuff><div insert-after-me></div><stuff></stuff></div>`);
var insertionElement = dom.childNodes[1];
parentView = createView([dom.childNodes[0]]);
protoView = new ProtoView(el('<div>hi</div>'), new ProtoRecordRange());
protoView = new ProtoView(el('<div>hi</div>'), new ProtoChangeDetector());
elementInjector = new ElementInjector(null, null, null);
viewPort = new ViewPort(parentView, insertionElement, protoView, elementInjector);
customViewWithOneNode = createView([el('<div>single</div>')]);
@ -117,25 +117,26 @@ export function main() {
viewPort.hydrate(new Injector([]), null);
var pv = new ProtoView(el('<div class="ng-binding">{{}}</div>'),
new ProtoRecordRange());
new ProtoChangeDetector());
pv.bindElement(new ProtoElementInjector(null, 1, [SomeDirective]));
pv.bindTextNode(0, parser.parseBinding('foo', null));
fancyView = pv.instantiate(null);
});
it('hydrating should update rootElementInjectors and parent RR', () => {
it('hydrating should update rootElementInjectors and parent change detector', () => {
viewPort.insert(fancyView);
ListWrapper.forEach(fancyView.rootElementInjectors, (inj) =>
expect(inj.parent).toBe(elementInjector));
expect(parentView.recordRange.findFirstEnabledRecord()).not.toBe(null);
expect(parentView.changeDetector.children.length).toBe(1);
});
it('dehydrating should update rootElementInjectors and parent RR', () => {
it('dehydrating should update rootElementInjectors and parent change detector', () => {
viewPort.insert(fancyView);
viewPort.remove();
ListWrapper.forEach(fancyView.rootElementInjectors, (inj) =>
expect(inj.parent).toBe(null));
expect(parentView.recordRange.findFirstEnabledRecord()).toBe(null);
expect(parentView.changeDetector.children.length).toBe(0);
expect(viewPort.length).toBe(0);
});
});