feat(change_detection): request a change detection check when an event happens
Closes #3679
This commit is contained in:
parent
823fa4689e
commit
5e6317fecc
|
@ -72,9 +72,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean {
|
handleEventInternal(eventName: string, elIndex: number, locals: Locals): boolean { return false; }
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
detectChanges(): void { this.runDetectChanges(false); }
|
detectChanges(): void { this.runDetectChanges(false); }
|
||||||
|
|
||||||
|
|
|
@ -118,8 +118,9 @@ export class BindingRecord {
|
||||||
}
|
}
|
||||||
|
|
||||||
static createForHostEvent(ast: AST, eventName: string,
|
static createForHostEvent(ast: AST, eventName: string,
|
||||||
directiveIndex: DirectiveIndex): BindingRecord {
|
directiveRecord: DirectiveRecord): BindingRecord {
|
||||||
|
var directiveIndex = directiveRecord.directiveIndex;
|
||||||
return new BindingRecord(EVENT, directiveIndex, ast, directiveIndex.elementIndex, null, null,
|
return new BindingRecord(EVENT, directiveIndex, ast, directiveIndex.elementIndex, null, null,
|
||||||
eventName, null, null, null);
|
eventName, null, null, directiveRecord);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -83,7 +83,7 @@ export class ChangeDetectorJITGenerator {
|
||||||
if (this.eventBindings.length > 0) {
|
if (this.eventBindings.length > 0) {
|
||||||
var handlers = this.eventBindings.map(eb => this._genEventBinding(eb)).join("\n");
|
var handlers = this.eventBindings.map(eb => this._genEventBinding(eb)).join("\n");
|
||||||
return `
|
return `
|
||||||
${this._typeName}.prototype.handleEvent = function(eventName, elIndex, locals) {
|
${this._typeName}.prototype.handleEventInternal = function(eventName, elIndex, locals) {
|
||||||
var ${this._names.getPreventDefaultAccesor()} = false;
|
var ${this._names.getPreventDefaultAccesor()} = false;
|
||||||
${this._names.genInitEventLocals()}
|
${this._names.genInitEventLocals()}
|
||||||
${handlers}
|
${handlers}
|
||||||
|
@ -106,13 +106,23 @@ export class ChangeDetectorJITGenerator {
|
||||||
_genEventBindingEval(eb: EventBinding, r: ProtoRecord): string {
|
_genEventBindingEval(eb: EventBinding, r: ProtoRecord): string {
|
||||||
if (r.lastInBinding) {
|
if (r.lastInBinding) {
|
||||||
var evalRecord = this._logic.genEventBindingEvalValue(eb, r);
|
var evalRecord = this._logic.genEventBindingEvalValue(eb, r);
|
||||||
|
var markPath = this._genMarkPathToRootAsCheckOnce(r);
|
||||||
var prevDefault = this._genUpdatePreventDefault(eb, r);
|
var prevDefault = this._genUpdatePreventDefault(eb, r);
|
||||||
return `${evalRecord}\n${prevDefault}`;
|
return `${evalRecord}\n${markPath}\n${prevDefault}`;
|
||||||
} else {
|
} else {
|
||||||
return this._logic.genEventBindingEvalValue(eb, r);
|
return this._logic.genEventBindingEvalValue(eb, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_genMarkPathToRootAsCheckOnce(r: ProtoRecord): string {
|
||||||
|
var br = r.bindingRecord;
|
||||||
|
if (br.isOnPushChangeDetection()) {
|
||||||
|
return `${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markPathToRootAsCheckOnce();`;
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_genUpdatePreventDefault(eb: EventBinding, r: ProtoRecord): string {
|
_genUpdatePreventDefault(eb: EventBinding, r: ProtoRecord): string {
|
||||||
var local = this._names.getEventLocalName(eb, r.selfIndex);
|
var local = this._names.getEventLocalName(eb, r.selfIndex);
|
||||||
return `if (${local} === false) { ${this._names.getPreventDefaultAccesor()} = true};`;
|
return `if (${local} === false) { ${this._names.getPreventDefaultAccesor()} = true};`;
|
||||||
|
|
|
@ -53,6 +53,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||||
var proto = eb.records[i];
|
var proto = eb.records[i];
|
||||||
var res = this._calculateCurrValue(proto, values, locals);
|
var res = this._calculateCurrValue(proto, values, locals);
|
||||||
if (proto.lastInBinding) {
|
if (proto.lastInBinding) {
|
||||||
|
this._markPathAsCheckOnce(proto);
|
||||||
return res;
|
return res;
|
||||||
} else {
|
} else {
|
||||||
this._writeSelf(proto, res, values);
|
this._writeSelf(proto, res, values);
|
||||||
|
@ -62,6 +63,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||||
throw new BaseException("Cannot be reached");
|
throw new BaseException("Cannot be reached");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_markPathAsCheckOnce(proto: ProtoRecord): void {
|
||||||
|
if (proto.bindingRecord.isOnPushChangeDetection()) {
|
||||||
|
var dir = proto.bindingRecord.directiveRecord;
|
||||||
|
this._getDetectorFor(dir.directiveIndex).markPathToRootAsCheckOnce();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_matchingEventBindings(eventName: string, elIndex: number): EventBinding[] {
|
_matchingEventBindings(eventName: string, elIndex: number): EventBinding[] {
|
||||||
return ListWrapper.filter(this.eventBindings,
|
return ListWrapper.filter(this.eventBindings,
|
||||||
eb => eb.eventName == eventName && eb.elIndex === elIndex);
|
eb => eb.eventName == eventName && eb.elIndex === elIndex);
|
||||||
|
|
|
@ -57,8 +57,7 @@ export class BindingRecordsCreator {
|
||||||
var directiveMetadata = allDirectiveMetadatas[dir.directiveIndex];
|
var directiveMetadata = allDirectiveMetadatas[dir.directiveIndex];
|
||||||
var dirRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
|
var dirRecord = this._getDirectiveRecord(boundElementIndex, i, directiveMetadata);
|
||||||
dir.eventBindings.forEach(heb => {
|
dir.eventBindings.forEach(heb => {
|
||||||
res.push(
|
res.push(BindingRecord.createForHostEvent(heb.source, heb.fullName, dirRecord));
|
||||||
BindingRecord.createForHostEvent(heb.source, heb.fullName, dirRecord.directiveIndex));
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -183,13 +183,23 @@ class _CodegenState {
|
||||||
String _genEventBindingEval(EventBinding eb, ProtoRecord r){
|
String _genEventBindingEval(EventBinding eb, ProtoRecord r){
|
||||||
if (r.lastInBinding) {
|
if (r.lastInBinding) {
|
||||||
var evalRecord = _logic.genEventBindingEvalValue(eb, r);
|
var evalRecord = _logic.genEventBindingEvalValue(eb, r);
|
||||||
|
var markPath = _genMarkPathToRootAsCheckOnce(r);
|
||||||
var prevDefault = _genUpdatePreventDefault(eb, r);
|
var prevDefault = _genUpdatePreventDefault(eb, r);
|
||||||
return "${evalRecord}\n${prevDefault}";
|
return "${evalRecord}\n${markPath}\n${prevDefault}";
|
||||||
} else {
|
} else {
|
||||||
return _logic.genEventBindingEvalValue(eb, r);
|
return _logic.genEventBindingEvalValue(eb, r);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _genMarkPathToRootAsCheckOnce(ProtoRecord r) {
|
||||||
|
var br = r.bindingRecord;
|
||||||
|
if (br.isOnPushChangeDetection()) {
|
||||||
|
return "${_names.getDetectorName(br.directiveRecord.directiveIndex)}.markPathToRootAsCheckOnce();";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String _genUpdatePreventDefault(EventBinding eb, ProtoRecord r) {
|
String _genUpdatePreventDefault(EventBinding eb, ProtoRecord r) {
|
||||||
var local = this._names.getEventLocalName(eb, r.selfIndex);
|
var local = this._names.getEventLocalName(eb, r.selfIndex);
|
||||||
return """if (${local} == false) { ${_names.getPreventDefaultAccesor()} = true; }""";
|
return """if (${local} == false) { ${_names.getPreventDefaultAccesor()} = true; }""";
|
||||||
|
|
|
@ -39,13 +39,14 @@ function _createEventRecords(expression: string): List<BindingRecord> {
|
||||||
return [BindingRecord.createForEvent(ast, eventName, 0)];
|
return [BindingRecord.createForEvent(ast, eventName, 0)];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _createHostEventRecords(expression: string): List<BindingRecord> {
|
function _createHostEventRecords(expression: string, directiveRecord: DirectiveRecord):
|
||||||
|
List<BindingRecord> {
|
||||||
var parts = expression.split("=");
|
var parts = expression.split("=");
|
||||||
var eventName = parts[0].substring(1, parts[0].length - 1);
|
var eventName = parts[0].substring(1, parts[0].length - 1);
|
||||||
var exp = parts[1].substring(1, parts[1].length - 1);
|
var exp = parts[1].substring(1, parts[1].length - 1);
|
||||||
|
|
||||||
var ast = _getParser().parseAction(exp, 'location');
|
var ast = _getParser().parseAction(exp, 'location');
|
||||||
return [BindingRecord.createForHostEvent(ast, eventName, new DirectiveIndex(0, 0))];
|
return [BindingRecord.createForHostEvent(ast, eventName, directiveRecord)];
|
||||||
}
|
}
|
||||||
|
|
||||||
function _convertLocalsToVariableBindings(locals: Locals): List<any> {
|
function _convertLocalsToVariableBindings(locals: Locals): List<any> {
|
||||||
|
@ -98,7 +99,7 @@ export function getDefinition(id: string): TestDefinition {
|
||||||
testDef = new TestDefinition(id, cdDef, null);
|
testDef = new TestDefinition(id, cdDef, null);
|
||||||
|
|
||||||
} else if (ListWrapper.indexOf(_availableHostEventDefinitions, id) >= 0) {
|
} else if (ListWrapper.indexOf(_availableHostEventDefinitions, id) >= 0) {
|
||||||
var eventRecords = _createHostEventRecords(id);
|
var eventRecords = _createHostEventRecords(id, _DirectiveUpdating.basicRecords[0]);
|
||||||
let cdDef = new ChangeDetectorDefinition(id, null, [], [], eventRecords,
|
let cdDef = new ChangeDetectorDefinition(id, null, [], [], eventRecords,
|
||||||
[_DirectiveUpdating.basicRecords[0]], true);
|
[_DirectiveUpdating.basicRecords[0]], true);
|
||||||
testDef = new TestDefinition(id, cdDef, null);
|
testDef = new TestDefinition(id, cdDef, null);
|
||||||
|
@ -165,12 +166,15 @@ class _ExpressionWithLocals {
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ExpressionWithMode {
|
class _ExpressionWithMode {
|
||||||
constructor(private _strategy: string, private _withRecords: boolean) {}
|
constructor(private _strategy: string, private _withRecords: boolean,
|
||||||
|
private _withEvents: boolean) {}
|
||||||
|
|
||||||
createChangeDetectorDefinition(): ChangeDetectorDefinition {
|
createChangeDetectorDefinition(): ChangeDetectorDefinition {
|
||||||
var variableBindings = [];
|
var variableBindings = [];
|
||||||
var bindingRecords = null;
|
var bindingRecords = [];
|
||||||
var directiveRecords = null;
|
var directiveRecords = [];
|
||||||
|
var eventRecords = [];
|
||||||
|
|
||||||
if (this._withRecords) {
|
if (this._withRecords) {
|
||||||
var dirRecordWithOnPush =
|
var dirRecordWithOnPush =
|
||||||
new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0), changeDetection: ON_PUSH});
|
new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0), changeDetection: ON_PUSH});
|
||||||
|
@ -183,8 +187,19 @@ class _ExpressionWithMode {
|
||||||
bindingRecords = [];
|
bindingRecords = [];
|
||||||
directiveRecords = [];
|
directiveRecords = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (this._withEvents) {
|
||||||
|
var dirRecordWithOnPush =
|
||||||
|
new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0), changeDetection: ON_PUSH});
|
||||||
|
directiveRecords = [dirRecordWithOnPush];
|
||||||
|
|
||||||
|
eventRecords =
|
||||||
|
ListWrapper.concat(_createEventRecords("(event)='false'"),
|
||||||
|
_createHostEventRecords("(host-event)='false'", dirRecordWithOnPush))
|
||||||
|
}
|
||||||
|
|
||||||
return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings,
|
return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings,
|
||||||
bindingRecords, [], directiveRecords, true);
|
bindingRecords, eventRecords, directiveRecords, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -192,9 +207,11 @@ class _ExpressionWithMode {
|
||||||
* Definitions in this map define conditions which allow testing various change detector modes.
|
* Definitions in this map define conditions which allow testing various change detector modes.
|
||||||
*/
|
*/
|
||||||
static availableDefinitions: StringMap<string, _ExpressionWithMode> = {
|
static availableDefinitions: StringMap<string, _ExpressionWithMode> = {
|
||||||
'emptyUsingDefaultStrategy': new _ExpressionWithMode(DEFAULT, false),
|
'emptyUsingDefaultStrategy': new _ExpressionWithMode(DEFAULT, false, false),
|
||||||
'emptyUsingOnPushStrategy': new _ExpressionWithMode(ON_PUSH, false),
|
'emptyUsingOnPushStrategy': new _ExpressionWithMode(ON_PUSH, false, false),
|
||||||
'onPushRecordsUsingDefaultStrategy': new _ExpressionWithMode(DEFAULT, true)
|
'onPushRecordsUsingDefaultStrategy': new _ExpressionWithMode(DEFAULT, true, false),
|
||||||
|
'onPushWithEvent': new _ExpressionWithMode(ON_PUSH, false, true),
|
||||||
|
'onPushWithHostEvent': new _ExpressionWithMode(ON_PUSH, false, true)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -716,6 +716,25 @@ export function main() {
|
||||||
|
|
||||||
expect(checkedDetector.mode).toEqual(CHECK_ONCE);
|
expect(checkedDetector.mode).toEqual(CHECK_ONCE);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should mark ON_PUSH detectors as CHECK_ONCE after an event', () => {
|
||||||
|
var cd = _createWithoutHydrate('onPushWithEvent').changeDetector;
|
||||||
|
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
|
||||||
|
cd.mode = CHECKED;
|
||||||
|
|
||||||
|
cd.handleEvent("event", 0, null);
|
||||||
|
|
||||||
|
expect(cd.mode).toEqual(CHECK_ONCE);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should mark ON_PUSH detectors as CHECK_ONCE after a host event', () => {
|
||||||
|
var cd = _createWithoutHydrate('onPushWithHostEvent').changeDetector;
|
||||||
|
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
|
||||||
|
|
||||||
|
cd.handleEvent("host-event", 0, null);
|
||||||
|
|
||||||
|
expect(checkedDetector.mode).toEqual(CHECK_ONCE);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -899,7 +918,6 @@ export function main() {
|
||||||
res = val.changeDetector.handleEvent("event", 0, locals);
|
res = val.changeDetector.handleEvent("event", 0, locals);
|
||||||
expect(res).toBe(false);
|
expect(res).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in New Issue