fix(change_detection): fixed reflect properties as attributes

Closes #3761
This commit is contained in:
vsavkin
2015-08-20 15:11:12 -07:00
committed by Victor Savkin
parent b6146394ae
commit a9ce454b21
28 changed files with 168 additions and 142 deletions
@@ -28,13 +28,13 @@ export function main() {
it("should return a proto change detector when one is available", () => {
var map = {'id': (def) => proto};
var cd = new PreGeneratedChangeDetection(map);
var cd = new PreGeneratedChangeDetection(null, map);
expect(cd.getProtoChangeDetector('id', def)).toBe(proto)
});
it("should delegate to dynamic change detection otherwise", () => {
var cd = new PreGeneratedChangeDetection({});
var cd = new PreGeneratedChangeDetection(null, {});
expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
});
});
@@ -66,7 +66,7 @@ export var PROP_NAME = 'propName';
* In this case, we expect `id` and `expression` to be the same string.
*/
export function getDefinition(id: string): TestDefinition {
var genConfig = new ChangeDetectorGenConfig(true, true);
var genConfig = new ChangeDetectorGenConfig(true, true, true);
var testDef = null;
if (StringMapWrapper.contains(_ExpressionWithLocals.availableDefinitions, id)) {
let val = StringMapWrapper.get(_ExpressionWithLocals.availableDefinitions, id);
@@ -110,6 +110,12 @@ export function getDefinition(id: string): TestDefinition {
var records = _createBindingRecords("a");
let cdDef = new ChangeDetectorDefinition(id, "ON_PUSH_OBSERVE", [], records, [], [], genConfig);
testDef = new TestDefinition(id, cdDef, null);
} else if (id == "updateElementProduction") {
var genConfig = new ChangeDetectorGenConfig(false, false, false);
var records = _createBindingRecords("name");
let cdDef = new ChangeDetectorDefinition(id, null, [], records, [], [], genConfig);
testDef = new TestDefinition(id, cdDef, null);
}
@@ -138,7 +144,7 @@ export function getAllDefinitions(): List<TestDefinition> {
ListWrapper.concat(allDefs, StringMapWrapper.keys(_DirectiveUpdating.availableDefinitions));
allDefs = ListWrapper.concat(allDefs, _availableEventDefinitions);
allDefs = ListWrapper.concat(allDefs, _availableHostEventDefinitions);
allDefs = ListWrapper.concat(allDefs, ["onPushObserve"]);
allDefs = ListWrapper.concat(allDefs, ["onPushObserve", "updateElementProduction"]);
return ListWrapper.map(allDefs, (id) => getDefinition(id));
}
@@ -150,7 +156,7 @@ class _ExpressionWithLocals {
var variableBindings = _convertLocalsToVariableBindings(this.locals);
var bindingRecords = _createBindingRecords(this._expression);
var directiveRecords = [];
var genConfig = new ChangeDetectorGenConfig(true, true);
var genConfig = new ChangeDetectorGenConfig(true, true, true);
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords,
[], directiveRecords, genConfig);
}
@@ -210,7 +216,7 @@ class _ExpressionWithMode {
_createHostEventRecords("(host-event)='false'", dirRecordWithOnPush))
}
var genConfig = new ChangeDetectorGenConfig(true, true);
var genConfig = new ChangeDetectorGenConfig(true, true, true);
return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings,
bindingRecords, eventRecords, directiveRecords, genConfig);
@@ -236,7 +242,7 @@ class _DirectiveUpdating {
createChangeDetectorDefinition(): ChangeDetectorDefinition {
var strategy = null;
var variableBindings = [];
var genConfig = new ChangeDetectorGenConfig(true, true);
var genConfig = new ChangeDetectorGenConfig(true, true, true);
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings,
this._bindingRecords, [], this._directiveRecords,
@@ -535,6 +535,31 @@ export function main() {
});
});
describe("logBindingUpdate", () => {
it('should be called for element updates in the dev mode', () => {
var person = new Person('bob');
var val = _createChangeDetector('name', person);
val.changeDetector.detectChanges();
expect(val.dispatcher.debugLog).toEqual(['propName=bob']);
});
it('should be called for directive updates in the dev mode', () => {
var val = _createWithoutHydrate('directNoDispatcher');
val.changeDetector.hydrate(_DEFAULT_CONTEXT, null,
new FakeDirectives([new TestDirective()], []), null);
val.changeDetector.detectChanges();
expect(val.dispatcher.debugLog).toEqual(["a=42"]);
});
it('should not be called in the prod mode', () => {
var person = new Person('bob');
var val = _createChangeDetector('updateElementProduction', person);
val.changeDetector.detectChanges();
expect(val.dispatcher.debugLog).toEqual([]);
});
});
describe('reading directives', () => {
it('should read directive properties', () => {
var directive = new TestDirective();
@@ -1123,7 +1148,8 @@ class FakeDirectives {
}
class TestDispatcher implements ChangeDispatcher {
log: List<string>;
log: string[];
debugLog: string[];
loggedValues: List<any>;
onAllChangesDoneCalled: boolean = false;
@@ -1131,6 +1157,7 @@ class TestDispatcher implements ChangeDispatcher {
clear() {
this.log = [];
this.debugLog = [];
this.loggedValues = [];
this.onAllChangesDoneCalled = true;
}
@@ -1140,6 +1167,8 @@ class TestDispatcher implements ChangeDispatcher {
this.loggedValues.push(value);
}
logBindingUpdate(target, value) { this.debugLog.push(`${target.name}=${this._asString(value)}`); }
notifyOnAllChangesDone() { this.onAllChangesDoneCalled = true; }
getDebugContext(a, b) { return null; }
@@ -58,7 +58,10 @@ import {
import {
PipeTransform,
ChangeDetectorRef,
ON_PUSH
ON_PUSH,
ChangeDetection,
DynamicChangeDetection,
ChangeDetectorGenConfig
} from 'angular2/src/change_detection/change_detection';
import {Directive, Component, View, ViewMetadata, Attribute, Query, Pipe} from 'angular2/metadata';
@@ -1518,6 +1521,30 @@ export function main() {
}));
});
describe('logging property updates', () => {
beforeEachBindings(() => [
bind(ChangeDetection)
.toValue(new DynamicChangeDetection(new ChangeDetectorGenConfig(true, true, true)))
]);
it('should reflect property values as attributes',
inject([TestComponentBuilder, AsyncTestCompleter], (tcb: TestComponentBuilder, async) => {
var tpl = '<div>' +
'<div my-dir [elprop]="ctxProp"></div>' +
'</div>';
tcb.overrideView(MyComp, new ViewMetadata({template: tpl, directives: [MyDir]}))
.createAsync(MyComp)
.then((rootTC) => {
rootTC.componentInstance.ctxProp = 'hello';
rootTC.detectChanges();
expect(DOM.getInnerHTML(rootTC.nativeElement))
.toContain('ng-reflect-dir-prop="hello"');
async.done();
});
}));
});
describe('different proto view storages', () => {
function runWithMode(mode: string) {
@@ -24,8 +24,6 @@ import {
RenderViewRef,
ViewEncapsulation
} from 'angular2/src/render/api';
import {DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from 'angular2/src/render/dom/dom_tokens';
import {bind} from 'angular2/di';
export function main() {
describe('DomRenderer integration', () => {
@@ -106,72 +104,6 @@ export function main() {
});
}));
it('should NOT reflect property values as attributes if flag is NOT set',
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y">',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
var el = DOM.childNodes(rootView.hostElement)[0];
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
.toEqual(null);
async.done();
});
}));
describe('reflection', () => {
beforeEachBindings(() => [bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(true)]);
it('should reflect property values as attributes if flag is set',
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y">',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
var el = DOM.childNodes(rootView.hostElement)[0];
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', '20');
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
.toEqual('20');
async.done();
});
}));
it('should reflect non-string property values as attributes if flag is set',
inject([AsyncTestCompleter, DomTestbed], (async, tb) => {
tb.compileAndMerge(someComponent,
[
new ViewDefinition({
componentId: 'someComponent',
template: '<input [title]="y">',
directives: []
})
])
.then((protoViewMergeMappings) => {
var rootView = tb.createView(protoViewMergeMappings);
var el = DOM.childNodes(rootView.hostElement)[0];
tb.renderer.setElementProperty(elRef(rootView.viewRef, 1), 'maxLength', 20);
expect(DOM.getAttribute(<HTMLInputElement>el, 'ng-reflect-max-length'))
.toEqual('20');
async.done();
});
}));
});
if (DOM.supportsDOMEvents()) {
it('should call actions on the element independent of the compilation',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {
@@ -58,6 +58,7 @@ class _MyComponent_ChangeDetector0
}
this.notifyDispatcher(l_interpolate1);
this.logBindingUpdate(l_interpolate1);
this.interpolate1 = l_interpolate1;
}