feat(change_detection): do not reparse AST when using generated detectors
This commit is contained in:
parent
b986c54079
commit
d2d0715568
|
@ -2,7 +2,7 @@ import {isPresent, isBlank, BaseException, StringWrapper} from 'angular2/src/fac
|
|||
import {List, ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {ChangeDetectionUtil} from './change_detection_util';
|
||||
import {ChangeDetectorRef} from './change_detector_ref';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
import {DirectiveIndex} from './directive_record';
|
||||
import {ChangeDetector, ChangeDispatcher} from './interfaces';
|
||||
import {Pipes} from './pipes';
|
||||
import {
|
||||
|
@ -10,8 +10,7 @@ import {
|
|||
ExpressionChangedAfterItHasBeenCheckedException,
|
||||
DehydratedException
|
||||
} from './exceptions';
|
||||
import {ProtoRecord} from './proto_record';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {Locals} from './parser/locals';
|
||||
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
|
@ -20,9 +19,8 @@ import {isObservable} from './observable_facade';
|
|||
var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
|
||||
|
||||
class _Context {
|
||||
constructor(public element: any, public componentElement: any, public instance: any,
|
||||
public context: any, public locals: any, public injector: any,
|
||||
public expression: any) {}
|
||||
constructor(public element: any, public componentElement: any, public context: any,
|
||||
public locals: any, public injector: any, public expression: any) {}
|
||||
}
|
||||
|
||||
export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
|
@ -35,24 +33,19 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||
// change detection will fail.
|
||||
alreadyChecked: any = false;
|
||||
context: T;
|
||||
directiveRecords: List<DirectiveRecord>;
|
||||
dispatcher: ChangeDispatcher;
|
||||
locals: Locals = null;
|
||||
mode: string = null;
|
||||
pipes: Pipes = null;
|
||||
firstProtoInCurrentBinding: number;
|
||||
protos: List<ProtoRecord>;
|
||||
propertyBindingIndex: number;
|
||||
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
subscriptions: any[];
|
||||
streams: any[];
|
||||
|
||||
constructor(public id: string, dispatcher: ChangeDispatcher, protos: List<ProtoRecord>,
|
||||
directiveRecords: List<DirectiveRecord>, public modeOnHydrate: string) {
|
||||
constructor(public id: string, public dispatcher: ChangeDispatcher,
|
||||
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
|
||||
public directiveIndices: DirectiveIndex[], public modeOnHydrate: string) {
|
||||
this.ref = new ChangeDetectorRef(this);
|
||||
this.directiveRecords = directiveRecords;
|
||||
this.dispatcher = dispatcher;
|
||||
this.protos = protos;
|
||||
}
|
||||
|
||||
addChild(cd: ChangeDetector): void {
|
||||
|
@ -99,7 +92,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||
// implementation of `detectChangesInRecordsInternal` which does the work of detecting changes
|
||||
// and which this method will call.
|
||||
// This method expects that `detectChangesInRecordsInternal` will set the property
|
||||
// `this.firstProtoInCurrentBinding` to the selfIndex of the first proto record. This is to
|
||||
// `this.propertyBindingIndex` to the propertyBindingIndex of the first proto record. This is to
|
||||
// facilitate error reporting.
|
||||
detectChangesInRecords(throwOnChange: boolean): void {
|
||||
if (!this.hydrated()) {
|
||||
|
@ -115,9 +108,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||
// Subclasses should override this method to perform any work necessary to detect and report
|
||||
// changes. For example, changes should be reported via `ChangeDetectionUtil.addChange`, lifecycle
|
||||
// methods should be called, etc.
|
||||
// This implementation should also set `this.firstProtoInCurrentBinding` to the selfIndex of the
|
||||
// first proto record
|
||||
// to facilitate error reporting. See {@link #detectChangesInRecords}.
|
||||
// This implementation should also set `this.propertyBindingIndex` to the propertyBindingIndex of
|
||||
// the
|
||||
// first proto record to facilitate error reporting. See {@link #detectChangesInRecords}.
|
||||
detectChangesInRecordsInternal(throwOnChange: boolean): void {}
|
||||
|
||||
// This method is not intended to be overridden. Subclasses should instead provide an
|
||||
|
@ -195,8 +188,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||
protected observe(value: any, index: number): any {
|
||||
if (isObservable(value)) {
|
||||
if (isBlank(this.subscriptions)) {
|
||||
this.subscriptions = ListWrapper.createFixedSize(this.protos.length + 1);
|
||||
this.streams = ListWrapper.createFixedSize(this.protos.length + 1);
|
||||
this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
|
||||
this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
|
||||
}
|
||||
if (isBlank(this.subscriptions[index])) {
|
||||
this.streams[index] = value.changes;
|
||||
|
@ -211,7 +204,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||
}
|
||||
|
||||
protected getDetectorFor(directives: any, index: number): ChangeDetector {
|
||||
return directives.getDetectorFor(this.directiveRecords[index].directiveIndex);
|
||||
return directives.getDetectorFor(this.directiveIndices[index]);
|
||||
}
|
||||
|
||||
protected notifyDispatcher(value: any): void {
|
||||
|
@ -223,31 +216,26 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
|||
if (isBlank(changes)) {
|
||||
changes = {};
|
||||
}
|
||||
changes[this._currentBinding().propertyName] =
|
||||
ChangeDetectionUtil.simpleChange(oldValue, newValue);
|
||||
changes[this._currentBinding().name] = ChangeDetectionUtil.simpleChange(oldValue, newValue);
|
||||
return changes;
|
||||
}
|
||||
|
||||
private _throwError(exception: any, stack: any): void {
|
||||
var proto = this._currentBindingProto();
|
||||
var c = this.dispatcher.getDebugContext(proto.bindingRecord.elementIndex, proto.directiveIndex);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.directive, c.context,
|
||||
c.locals, c.injector, proto.expressionAsString) :
|
||||
var c = this.dispatcher.getDebugContext(this._currentBinding().elementIndex, null);
|
||||
var context = isPresent(c) ? new _Context(c.element, c.componentElement, c.context, c.locals,
|
||||
c.injector, this._currentBinding().debug) :
|
||||
null;
|
||||
throw new ChangeDetectionError(proto, exception, stack, context);
|
||||
throw new ChangeDetectionError(this._currentBinding().debug, exception, stack, context);
|
||||
}
|
||||
|
||||
protected throwOnChangeError(oldValue: any, newValue: any): void {
|
||||
var change = ChangeDetectionUtil.simpleChange(oldValue, newValue);
|
||||
throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBindingProto(), change,
|
||||
null);
|
||||
throw new ExpressionChangedAfterItHasBeenCheckedException(this._currentBinding().debug,
|
||||
oldValue, newValue, null);
|
||||
}
|
||||
|
||||
protected throwDehydratedError(): void { throw new DehydratedException(); }
|
||||
|
||||
private _currentBinding(): BindingRecord { return this._currentBindingProto().bindingRecord; }
|
||||
|
||||
private _currentBindingProto(): ProtoRecord {
|
||||
return ChangeDetectionUtil.protoByIndex(this.protos, this.firstProtoInCurrentBinding);
|
||||
private _currentBinding(): BindingTarget {
|
||||
return this.bindingTargets[this.propertyBindingIndex];
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,8 +3,10 @@ import {SetterFn} from 'angular2/src/reflection/types';
|
|||
import {AST} from './parser/ast';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
|
||||
const DIRECTIVE = "directive";
|
||||
const DIRECTIVE_LIFECYCLE = "directiveLifecycle";
|
||||
const BINDING = "native";
|
||||
|
||||
const DIRECTIVE = "directive";
|
||||
const ELEMENT_PROPERTY = "elementProperty";
|
||||
const ELEMENT_ATTRIBUTE = "elementAttribute";
|
||||
const ELEMENT_CLASS = "elementClass";
|
||||
|
@ -13,24 +15,12 @@ const TEXT_NODE = "textNode";
|
|||
const EVENT = "event";
|
||||
const HOST_EVENT = "hostEvent";
|
||||
|
||||
export class BindingRecord {
|
||||
constructor(public mode: string, public implicitReceiver: any, public ast: AST,
|
||||
public elementIndex: number, public propertyName: string, public propertyUnit: string,
|
||||
public eventName: string, public setter: SetterFn, public lifecycleEvent: string,
|
||||
public directiveRecord: DirectiveRecord) {}
|
||||
|
||||
callOnChange(): boolean {
|
||||
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
|
||||
}
|
||||
|
||||
isDefaultChangeDetection(): boolean {
|
||||
return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection();
|
||||
}
|
||||
export class BindingTarget {
|
||||
constructor(public mode: string, public elementIndex: number, public name: string,
|
||||
public unit: string, public debug: string) {}
|
||||
|
||||
isDirective(): boolean { return this.mode === DIRECTIVE; }
|
||||
|
||||
isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; }
|
||||
|
||||
isElementProperty(): boolean { return this.mode === ELEMENT_PROPERTY; }
|
||||
|
||||
isElementAttribute(): boolean { return this.mode === ELEMENT_ATTRIBUTE; }
|
||||
|
@ -40,87 +30,118 @@ export class BindingRecord {
|
|||
isElementStyle(): boolean { return this.mode === ELEMENT_STYLE; }
|
||||
|
||||
isTextNode(): boolean { return this.mode === TEXT_NODE; }
|
||||
}
|
||||
|
||||
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
|
||||
directiveRecord: DirectiveRecord): BindingRecord {
|
||||
return new BindingRecord(DIRECTIVE, 0, ast, 0, propertyName, null, null, setter, null,
|
||||
directiveRecord);
|
||||
export class BindingRecord {
|
||||
constructor(public mode: string, public target: BindingTarget, public implicitReceiver: any,
|
||||
public ast: AST, public setter: SetterFn, public lifecycleEvent: string,
|
||||
public directiveRecord: DirectiveRecord) {}
|
||||
|
||||
isDirectiveLifecycle(): boolean { return this.mode === DIRECTIVE_LIFECYCLE; }
|
||||
|
||||
callOnChange(): boolean {
|
||||
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
|
||||
}
|
||||
|
||||
isDefaultChangeDetection(): boolean {
|
||||
return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection();
|
||||
}
|
||||
|
||||
|
||||
static createDirectiveOnCheck(directiveRecord: DirectiveRecord): BindingRecord {
|
||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onCheck",
|
||||
directiveRecord);
|
||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onCheck", directiveRecord);
|
||||
}
|
||||
|
||||
static createDirectiveOnInit(directiveRecord: DirectiveRecord): BindingRecord {
|
||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onInit",
|
||||
directiveRecord);
|
||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onInit", directiveRecord);
|
||||
}
|
||||
|
||||
static createDirectiveOnChange(directiveRecord: DirectiveRecord): BindingRecord {
|
||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, 0, null, 0, null, null, null, null, "onChange",
|
||||
directiveRecord);
|
||||
return new BindingRecord(DIRECTIVE_LIFECYCLE, null, 0, null, null, "onChange", directiveRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
|
||||
directiveRecord: DirectiveRecord): BindingRecord {
|
||||
var t = new BindingTarget(DIRECTIVE, null, propertyName, null, ast.toString());
|
||||
return new BindingRecord(DIRECTIVE, t, 0, ast, setter, null, directiveRecord);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static createForElementProperty(ast: AST, elementIndex: number,
|
||||
propertyName: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_PROPERTY, 0, ast, elementIndex, propertyName, null, null, null,
|
||||
null, null);
|
||||
var t = new BindingTarget(ELEMENT_PROPERTY, elementIndex, propertyName, null, ast.toString());
|
||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForElementAttribute(ast: AST, elementIndex: number,
|
||||
attributeName: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_ATTRIBUTE, 0, ast, elementIndex, attributeName, null, null,
|
||||
null, null, null);
|
||||
var t = new BindingTarget(ELEMENT_ATTRIBUTE, elementIndex, attributeName, null, ast.toString());
|
||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForElementClass(ast: AST, elementIndex: number, className: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_CLASS, 0, ast, elementIndex, className, null, null, null, null,
|
||||
null);
|
||||
var t = new BindingTarget(ELEMENT_CLASS, elementIndex, className, null, ast.toString());
|
||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForElementStyle(ast: AST, elementIndex: number, styleName: string,
|
||||
unit: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_STYLE, 0, ast, elementIndex, styleName, unit, null, null, null,
|
||||
null);
|
||||
var t = new BindingTarget(ELEMENT_STYLE, elementIndex, styleName, unit, ast.toString());
|
||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static createForHostProperty(directiveIndex: DirectiveIndex, ast: AST,
|
||||
propertyName: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_PROPERTY, directiveIndex, ast, directiveIndex.elementIndex,
|
||||
propertyName, null, null, null, null, null);
|
||||
var t = new BindingTarget(ELEMENT_PROPERTY, directiveIndex.elementIndex, propertyName, null,
|
||||
ast.toString());
|
||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForHostAttribute(directiveIndex: DirectiveIndex, ast: AST,
|
||||
attributeName: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_ATTRIBUTE, directiveIndex, ast, directiveIndex.elementIndex,
|
||||
attributeName, null, null, null, null, null);
|
||||
var t = new BindingTarget(ELEMENT_ATTRIBUTE, directiveIndex.elementIndex, attributeName, null,
|
||||
ast.toString());
|
||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForHostClass(directiveIndex: DirectiveIndex, ast: AST,
|
||||
className: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_CLASS, directiveIndex, ast, directiveIndex.elementIndex,
|
||||
className, null, null, null, null, null);
|
||||
var t = new BindingTarget(ELEMENT_CLASS, directiveIndex.elementIndex, className, null,
|
||||
ast.toString());
|
||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForHostStyle(directiveIndex: DirectiveIndex, ast: AST, styleName: string,
|
||||
unit: string): BindingRecord {
|
||||
return new BindingRecord(ELEMENT_STYLE, directiveIndex, ast, directiveIndex.elementIndex,
|
||||
styleName, unit, null, null, null, null);
|
||||
var t = new BindingTarget(ELEMENT_STYLE, directiveIndex.elementIndex, styleName, unit,
|
||||
ast.toString());
|
||||
return new BindingRecord(BINDING, t, directiveIndex, ast, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static createForTextNode(ast: AST, elementIndex: number): BindingRecord {
|
||||
return new BindingRecord(TEXT_NODE, 0, ast, elementIndex, null, null, null, null, null, null);
|
||||
var t = new BindingTarget(TEXT_NODE, elementIndex, null, null, ast.toString());
|
||||
return new BindingRecord(BINDING, t, 0, ast, null, null, null);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static createForEvent(ast: AST, eventName: string, elementIndex: number): BindingRecord {
|
||||
return new BindingRecord(EVENT, 0, ast, elementIndex, null, null, eventName, null, null, null);
|
||||
var t = new BindingTarget(EVENT, elementIndex, eventName, null, ast.toString());
|
||||
return new BindingRecord(EVENT, t, 0, ast, null, null, null);
|
||||
}
|
||||
|
||||
static createForHostEvent(ast: AST, eventName: string,
|
||||
directiveRecord: DirectiveRecord): BindingRecord {
|
||||
var directiveIndex = directiveRecord.directiveIndex;
|
||||
return new BindingRecord(EVENT, directiveIndex, ast, directiveIndex.elementIndex, null, null,
|
||||
eventName, null, null, directiveRecord);
|
||||
var t =
|
||||
new BindingTarget(HOST_EVENT, directiveIndex.elementIndex, eventName, null, ast.toString());
|
||||
return new BindingRecord(HOST_EVENT, t, directiveIndex, ast, null, null, directiveRecord);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,7 +38,7 @@ export {
|
|||
} from './interfaces';
|
||||
export {CHECK_ONCE, CHECK_ALWAYS, DETACHED, CHECKED, ON_PUSH, DEFAULT} from './constants';
|
||||
export {DynamicProtoChangeDetector} from './proto_change_detector';
|
||||
export {BindingRecord} from './binding_record';
|
||||
export {BindingRecord, BindingTarget} from './binding_record';
|
||||
export {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
export {DynamicChangeDetector} from './dynamic_change_detector';
|
||||
export {ChangeDetectorRef} from './change_detector_ref';
|
||||
|
@ -93,13 +93,14 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
|
|||
|
||||
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
|
||||
|
||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
var id = definition.id;
|
||||
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
if (StringMapWrapper.contains(this._protoChangeDetectorFactories, id)) {
|
||||
return StringMapWrapper.get(this._protoChangeDetectorFactories, id)(definition);
|
||||
}
|
||||
return this._dynamicChangeDetection.createProtoChangeDetector(definition);
|
||||
return this._dynamicChangeDetection.getProtoChangeDetector(id, definition);
|
||||
}
|
||||
|
||||
get generateDetectors(): boolean { return true; }
|
||||
}
|
||||
|
||||
|
||||
|
@ -110,9 +111,11 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
|
|||
*/
|
||||
@Injectable()
|
||||
export class DynamicChangeDetection extends ChangeDetection {
|
||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
return new DynamicProtoChangeDetector(definition);
|
||||
}
|
||||
|
||||
get generateDetectors(): boolean { return true; }
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -126,7 +129,9 @@ export class DynamicChangeDetection extends ChangeDetection {
|
|||
export class JitChangeDetection extends ChangeDetection {
|
||||
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
|
||||
|
||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
return new JitProtoChangeDetector(definition);
|
||||
}
|
||||
|
||||
get generateDetectors(): boolean { return true; }
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import {ProtoRecord, RecordType} from './proto_record';
|
|||
import {CodegenNameUtil, sanitizeName} from './codegen_name_util';
|
||||
import {CodegenLogicUtil} from './codegen_logic_util';
|
||||
import {EventBinding} from './event_binding';
|
||||
import {BindingTarget} from './binding_record';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -30,9 +31,10 @@ export class ChangeDetectorJITGenerator {
|
|||
_names: CodegenNameUtil;
|
||||
_typeName: string;
|
||||
|
||||
constructor(public id: string, private changeDetectionStrategy: string,
|
||||
public records: List<ProtoRecord>, public eventBindings: EventBinding[],
|
||||
public directiveRecords: List<any>, private generateCheckNoChanges: boolean) {
|
||||
constructor(private id: string, private changeDetectionStrategy: string,
|
||||
private records: List<ProtoRecord>, private propertyBindingTargets: BindingTarget[],
|
||||
private eventBindings: EventBinding[], private directiveRecords: List<any>,
|
||||
private devMode: boolean) {
|
||||
this._names =
|
||||
new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL);
|
||||
this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy);
|
||||
|
@ -41,9 +43,10 @@ export class ChangeDetectorJITGenerator {
|
|||
|
||||
generate(): Function {
|
||||
var classDefinition = `
|
||||
var ${this._typeName} = function ${this._typeName}(dispatcher, protos, directiveRecords) {
|
||||
var ${this._typeName} = function ${this._typeName}(dispatcher) {
|
||||
${ABSTRACT_CHANGE_DETECTOR}.call(
|
||||
this, ${JSON.stringify(this.id)}, dispatcher, protos, directiveRecords,
|
||||
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
|
||||
${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices,
|
||||
"${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}");
|
||||
this.dehydrateDirectives(false);
|
||||
}
|
||||
|
@ -70,13 +73,27 @@ export class ChangeDetectorJITGenerator {
|
|||
|
||||
${this._maybeGenDehydrateDirectives()}
|
||||
|
||||
${this._genPropertyBindingTargets()};
|
||||
|
||||
${this._genDirectiveIndices()};
|
||||
|
||||
return function(dispatcher) {
|
||||
return new ${this._typeName}(dispatcher, protos, directiveRecords);
|
||||
return new ${this._typeName}(dispatcher);
|
||||
}
|
||||
`;
|
||||
return new Function('AbstractChangeDetector', 'ChangeDetectionUtil', 'protos',
|
||||
'directiveRecords', classDefinition)(
|
||||
AbstractChangeDetector, ChangeDetectionUtil, this.records, this.directiveRecords);
|
||||
|
||||
return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector,
|
||||
ChangeDetectionUtil);
|
||||
}
|
||||
|
||||
_genPropertyBindingTargets(): string {
|
||||
var targets = this._logic.genPropertyBindingTargets(this.propertyBindingTargets, true);
|
||||
return `${this._typeName}.gen_propertyBindingTargets = ${targets};`;
|
||||
}
|
||||
|
||||
_genDirectiveIndices(): string {
|
||||
var indices = this._logic.genDirectiveIndices(this.directiveRecords);
|
||||
return `${this._typeName}.gen_directiveIndices = ${indices};`;
|
||||
}
|
||||
|
||||
_maybeGenHandleEventInternal(): string {
|
||||
|
@ -156,7 +173,7 @@ export class ChangeDetectorJITGenerator {
|
|||
var lines = ListWrapper.createFixedSize(directiveFieldNames.length);
|
||||
for (var i = 0, iLen = directiveFieldNames.length; i < iLen; ++i) {
|
||||
lines[i] = `${directiveFieldNames[i]} = directives.getDirectiveFor(
|
||||
${this._names.getDirectivesAccessorName()}[${i}].directiveIndex);`;
|
||||
${this._names.getDirectivesAccessorName()}[${i}]);`;
|
||||
}
|
||||
return lines.join('\n');
|
||||
}
|
||||
|
@ -284,9 +301,9 @@ export class ChangeDetectorJITGenerator {
|
|||
var oldValue = this._names.getFieldName(r.selfIndex);
|
||||
|
||||
var br = r.bindingRecord;
|
||||
if (br.isDirective()) {
|
||||
if (br.target.isDirective()) {
|
||||
var directiveProperty =
|
||||
`${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.propertyName}`;
|
||||
`${this._names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}`;
|
||||
return `
|
||||
${this._genThrowOnChangeCheck(oldValue, newValue)}
|
||||
${directiveProperty} = ${newValue};
|
||||
|
@ -301,7 +318,7 @@ export class ChangeDetectorJITGenerator {
|
|||
}
|
||||
|
||||
_genThrowOnChangeCheck(oldValue: string, newValue: string): string {
|
||||
if (this.generateCheckNoChanges) {
|
||||
if (this.devMode) {
|
||||
return `
|
||||
if(throwOnChange) {
|
||||
this.throwOnChangeError(${oldValue}, ${newValue});
|
||||
|
@ -313,7 +330,7 @@ export class ChangeDetectorJITGenerator {
|
|||
}
|
||||
|
||||
_genCheckNoChanges(): string {
|
||||
if (this.generateCheckNoChanges) {
|
||||
if (this.devMode) {
|
||||
return `${this._typeName}.prototype.checkNoChanges = function() { this.runDetectChanges(true); }`;
|
||||
} else {
|
||||
return '';
|
||||
|
@ -330,7 +347,9 @@ export class ChangeDetectorJITGenerator {
|
|||
_maybeFirstInBinding(r: ProtoRecord): string {
|
||||
var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
|
||||
var firstInBindng = isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
|
||||
return firstInBindng ? `${this._names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};` : '';
|
||||
return firstInBindng && !r.bindingRecord.isDirectiveLifecycle() ?
|
||||
`${this._names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};` :
|
||||
'';
|
||||
}
|
||||
|
||||
_maybeGenLastInDirective(r: ProtoRecord): string {
|
||||
|
|
|
@ -16,6 +16,8 @@ import {
|
|||
isDefaultChangeDetectionStrategy
|
||||
} from './constants';
|
||||
import {implementsOnDestroy} from './pipe_lifecycle_reflector';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {DirectiveIndex} from './directive_record';
|
||||
|
||||
|
||||
/**
|
||||
|
@ -201,4 +203,13 @@ export class ChangeDetectionUtil {
|
|||
pipe.onDestroy();
|
||||
}
|
||||
}
|
||||
|
||||
static bindingTarget(mode: string, elementIndex: number, name: string, unit: string,
|
||||
debug: string): BindingTarget {
|
||||
return new BindingTarget(mode, elementIndex, name, unit, debug);
|
||||
}
|
||||
|
||||
static directiveIndex(elementIndex: number, directiveIndex: number): DirectiveIndex {
|
||||
return new DirectiveIndex(elementIndex, directiveIndex);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,8 +44,8 @@ export function coalesce(records: ProtoRecord[]): ProtoRecord[] {
|
|||
|
||||
function _selfRecord(r: ProtoRecord, contextIndex: number, selfIndex: number): ProtoRecord {
|
||||
return new ProtoRecord(RecordType.SELF, "self", null, [], r.fixedArgs, contextIndex,
|
||||
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
|
||||
r.lastInBinding, r.lastInDirective, false, false);
|
||||
r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding,
|
||||
r.lastInDirective, false, false, r.propertyBindingIndex);
|
||||
}
|
||||
|
||||
function _findMatching(r: ProtoRecord, rs: List<ProtoRecord>) {
|
||||
|
@ -70,9 +70,9 @@ function _replaceIndices(r: ProtoRecord, selfIndex: number, indexMap: Map<any, a
|
|||
var args = ListWrapper.map(r.args, (a) => _map(indexMap, a));
|
||||
var contextIndex = _map(indexMap, r.contextIndex);
|
||||
return new ProtoRecord(r.mode, r.name, r.funcOrValue, args, r.fixedArgs, contextIndex,
|
||||
r.directiveIndex, selfIndex, r.bindingRecord, r.expressionAsString,
|
||||
r.lastInBinding, r.lastInDirective, r.argumentToPureFunction,
|
||||
r.referencedBySelf);
|
||||
r.directiveIndex, selfIndex, r.bindingRecord, r.lastInBinding,
|
||||
r.lastInDirective, r.argumentToPureFunction, r.referencedBySelf,
|
||||
r.propertyBindingIndex);
|
||||
}
|
||||
|
||||
function _map(indexMap: Map<any, any>, value: number) {
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {BaseException, Json, StringWrapper} from 'angular2/src/facade/lang';
|
||||
import {BaseException, Json, StringWrapper, isPresent, isBlank} from 'angular2/src/facade/lang';
|
||||
import {CodegenNameUtil} from './codegen_name_util';
|
||||
import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
|
||||
/**
|
||||
|
@ -123,6 +124,23 @@ export class CodegenLogicUtil {
|
|||
}
|
||||
}
|
||||
|
||||
genPropertyBindingTargets(propertyBindingTargets: BindingTarget[], devMode: boolean): string {
|
||||
var bs = propertyBindingTargets.map(b => {
|
||||
if (isBlank(b)) return "null";
|
||||
|
||||
var debug = devMode ? codify(b.debug) : "null";
|
||||
return `${this._utilName}.bindingTarget(${codify(b.mode)}, ${b.elementIndex}, ${codify(b.name)}, ${codify(b.unit)}, ${debug})`;
|
||||
});
|
||||
return `[${bs.join(", ")}]`;
|
||||
}
|
||||
|
||||
genDirectiveIndices(directiveRecords: DirectiveRecord[]): string {
|
||||
var bs = directiveRecords.map(
|
||||
b =>
|
||||
`${this._utilName}.directiveIndex(${b.directiveIndex.elementIndex}, ${b.directiveIndex.directiveIndex})`);
|
||||
return `[${bs.join(", ")}]`;
|
||||
}
|
||||
|
||||
_genInterpolation(protoRec: ProtoRecord): string {
|
||||
var iVals = [];
|
||||
for (var i = 0; i < protoRec.args.length; ++i) {
|
||||
|
|
|
@ -10,8 +10,8 @@ import {EventBinding} from './event_binding';
|
|||
// detection will fail.
|
||||
const _ALREADY_CHECKED_ACCESSOR = "alreadyChecked";
|
||||
const _CONTEXT_ACCESSOR = "context";
|
||||
const _FIRST_PROTO_IN_CURRENT_BINDING = "firstProtoInCurrentBinding";
|
||||
const _DIRECTIVES_ACCESSOR = "directiveRecords";
|
||||
const _PROP_BINDING_INDEX = "propertyBindingIndex";
|
||||
const _DIRECTIVES_ACCESSOR = "directiveIndices";
|
||||
const _DISPATCHER_ACCESSOR = "dispatcher";
|
||||
const _LOCALS_ACCESSOR = "locals";
|
||||
const _MODE_ACCESSOR = "mode";
|
||||
|
@ -79,9 +79,7 @@ export class CodegenNameUtil {
|
|||
|
||||
getModeName(): string { return this._addFieldPrefix(_MODE_ACCESSOR); }
|
||||
|
||||
getFirstProtoInCurrentBinding(): string {
|
||||
return this._addFieldPrefix(_FIRST_PROTO_IN_CURRENT_BINDING);
|
||||
}
|
||||
getPropertyBindingIndex(): string { return this._addFieldPrefix(_PROP_BINDING_INDEX); }
|
||||
|
||||
getLocalName(idx: number): string { return `l_${this._sanitizedNames[idx]}`; }
|
||||
|
||||
|
|
|
@ -9,11 +9,11 @@ import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/faca
|
|||
|
||||
import {AbstractChangeDetector} from './abstract_change_detector';
|
||||
import {EventBinding} from './event_binding';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {BindingRecord, BindingTarget} from './binding_record';
|
||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
import {Locals} from './parser/locals';
|
||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
||||
|
||||
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
|
||||
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
|
@ -23,12 +23,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|||
prevContexts: List<any>;
|
||||
directives: any = null;
|
||||
|
||||
constructor(id: string, changeDetectionStrategy: string, dispatcher: any,
|
||||
protos: List<ProtoRecord>, public eventBindings: EventBinding[],
|
||||
directiveRecords: List<any>) {
|
||||
super(id, dispatcher, protos, directiveRecords,
|
||||
ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy));
|
||||
var len = protos.length + 1;
|
||||
constructor(id: string, dispatcher: any, numberOfPropertyProtoRecords: number,
|
||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
||||
modeOnHydrate: string, private records: ProtoRecord[],
|
||||
private eventBindings: EventBinding[], private directiveRecords: DirectiveRecord[]) {
|
||||
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
|
||||
modeOnHydrate);
|
||||
var len = records.length + 1;
|
||||
this.values = ListWrapper.createFixedSize(len);
|
||||
this.localPipes = ListWrapper.createFixedSize(len);
|
||||
this.prevContexts = ListWrapper.createFixedSize(len);
|
||||
|
@ -109,7 +110,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|||
checkNoChanges(): void { this.runDetectChanges(true); }
|
||||
|
||||
detectChangesInRecordsInternal(throwOnChange: boolean) {
|
||||
var protos = this.protos;
|
||||
var protos = this.records;
|
||||
|
||||
var changes = null;
|
||||
var isChanged = false;
|
||||
|
@ -119,7 +120,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|||
var directiveRecord = bindingRecord.directiveRecord;
|
||||
|
||||
if (this._firstInBinding(proto)) {
|
||||
this.firstProtoInCurrentBinding = proto.selfIndex;
|
||||
this.propertyBindingIndex = proto.propertyBindingIndex;
|
||||
}
|
||||
|
||||
if (proto.isLifeCycleRecord()) {
|
||||
|
@ -154,7 +155,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|||
}
|
||||
|
||||
_firstInBinding(r: ProtoRecord): boolean {
|
||||
var prev = ChangeDetectionUtil.protoByIndex(this.protos, r.selfIndex - 1);
|
||||
var prev = ChangeDetectionUtil.protoByIndex(this.records, r.selfIndex - 1);
|
||||
return isBlank(prev) || prev.bindingRecord !== r.bindingRecord;
|
||||
}
|
||||
|
||||
|
@ -171,7 +172,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
|||
|
||||
_updateDirectiveOrElement(change, bindingRecord) {
|
||||
if (isBlank(bindingRecord.directiveRecord)) {
|
||||
this.dispatcher.notifyOnBinding(bindingRecord, change.currentValue);
|
||||
super.notifyDispatcher(change.currentValue);
|
||||
} else {
|
||||
var directiveIndex = bindingRecord.directiveRecord.directiveIndex;
|
||||
bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import {ProtoRecord} from './proto_record';
|
||||
import {BaseException} from "angular2/src/facade/lang";
|
||||
|
||||
/**
|
||||
|
@ -11,9 +10,9 @@ import {BaseException} from "angular2/src/facade/lang";
|
|||
* This exception is only thrown in dev mode.
|
||||
*/
|
||||
export class ExpressionChangedAfterItHasBeenCheckedException extends BaseException {
|
||||
constructor(proto: ProtoRecord, change: any, context: any) {
|
||||
super(`Expression '${proto.expressionAsString}' has changed after it was checked. ` +
|
||||
`Previous value: '${change.previousValue}'. Current value: '${change.currentValue}'`);
|
||||
constructor(exp: string, oldValue: any, currValue: any, context: any) {
|
||||
super(`Expression '${exp}' has changed after it was checked. ` +
|
||||
`Previous value: '${oldValue}'. Current value: '${currValue}'`);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -28,10 +27,9 @@ export class ChangeDetectionError extends BaseException {
|
|||
*/
|
||||
location: string;
|
||||
|
||||
constructor(proto: ProtoRecord, originalException: any, originalStack: any, context: any) {
|
||||
super(`${originalException} in [${proto.expressionAsString}]`, originalException, originalStack,
|
||||
context);
|
||||
this.location = proto.expressionAsString;
|
||||
constructor(exp: string, originalException: any, originalStack: any, context: any) {
|
||||
super(`${originalException} in [${exp}]`, originalException, originalStack, context);
|
||||
this.location = exp;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import {List} from 'angular2/src/facade/collection';
|
||||
import {CONST} from 'angular2/src/facade/lang';
|
||||
import {Locals} from './parser/locals';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {BindingTarget, BindingRecord} from './binding_record';
|
||||
import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
import {ChangeDetectorRef} from './change_detector_ref';
|
||||
|
||||
|
@ -32,9 +32,11 @@ import {ChangeDetectorRef} from './change_detector_ref';
|
|||
*/
|
||||
@CONST()
|
||||
export class ChangeDetection {
|
||||
createProtoChangeDetector(definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
|
||||
return null;
|
||||
}
|
||||
|
||||
get generateDetectors(): boolean { return null; }
|
||||
}
|
||||
|
||||
export class DebugContext {
|
||||
|
@ -44,7 +46,7 @@ export class DebugContext {
|
|||
|
||||
export interface ChangeDispatcher {
|
||||
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
|
||||
notifyOnBinding(bindingRecord: BindingRecord, value: any): void;
|
||||
notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
|
||||
notifyOnAllChangesDone(): void;
|
||||
}
|
||||
|
||||
|
@ -72,5 +74,5 @@ export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher)
|
|||
export class ChangeDetectorDefinition {
|
||||
constructor(public id: string, public strategy: string, public variableNames: List<string>,
|
||||
public bindingRecords: BindingRecord[], public eventRecords: BindingRecord[],
|
||||
public directiveRecords: DirectiveRecord[], public generateCheckNoChanges: boolean) {}
|
||||
public directiveRecords: DirectiveRecord[], public devMode: boolean) {}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import {ListWrapper} from 'angular2/src/facade/collection';
|
||||
import {isPresent} from 'angular2/src/facade/lang';
|
||||
|
||||
import {ProtoChangeDetector, ChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
||||
import {ChangeDetectorJITGenerator} from './change_detection_jit_generator';
|
||||
|
@ -20,9 +21,11 @@ export class JitProtoChangeDetector implements ProtoChangeDetector {
|
|||
_createFactory(definition: ChangeDetectorDefinition) {
|
||||
var propertyBindingRecords = createPropertyRecords(definition);
|
||||
var eventBindingRecords = createEventRecords(definition);
|
||||
var propertyBindingTargets = this.definition.bindingRecords.map(b => b.target);
|
||||
|
||||
return new ChangeDetectorJITGenerator(
|
||||
definition.id, definition.strategy, propertyBindingRecords, eventBindingRecords,
|
||||
this.definition.directiveRecords, this.definition.generateCheckNoChanges)
|
||||
definition.id, definition.strategy, propertyBindingRecords, propertyBindingTargets,
|
||||
eventBindingRecords, this.definition.directiveRecords, this.definition.devMode)
|
||||
.generate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
library angular2.src.change_detection.pregen_proto_change_detector;
|
||||
|
||||
import 'package:angular2/src/change_detection/coalesce.dart';
|
||||
import 'package:angular2/src/change_detection/directive_record.dart';
|
||||
import 'package:angular2/src/change_detection/interfaces.dart';
|
||||
import 'package:angular2/src/change_detection/proto_change_detector.dart';
|
||||
import 'package:angular2/src/change_detection/proto_record.dart';
|
||||
import 'package:angular2/src/facade/lang.dart' show looseIdentical;
|
||||
|
||||
export 'dart:core' show List;
|
||||
|
@ -26,8 +22,7 @@ export 'package:angular2/src/facade/lang.dart' show looseIdentical;
|
|||
typedef ProtoChangeDetector PregenProtoChangeDetectorFactory(
|
||||
ChangeDetectorDefinition definition);
|
||||
|
||||
typedef ChangeDetector InstantiateMethod(dynamic dispatcher,
|
||||
List<ProtoRecord> protoRecords, List<DirectiveRecord> directiveRecords);
|
||||
typedef ChangeDetector InstantiateMethod(dynamic dispatcher);
|
||||
|
||||
/// Implementation of [ProtoChangeDetector] for use by pre-generated change
|
||||
/// detectors in Angular 2 Dart.
|
||||
|
@ -41,31 +36,18 @@ class PregenProtoChangeDetector extends ProtoChangeDetector {
|
|||
/// Closure used to generate an actual [ChangeDetector].
|
||||
final InstantiateMethod _instantiateMethod;
|
||||
|
||||
// [ChangeDetector] dependencies.
|
||||
final List<ProtoRecord> _protoRecords;
|
||||
final List<DirectiveRecord> _directiveRecords;
|
||||
|
||||
/// Internal ctor.
|
||||
PregenProtoChangeDetector._(this.id, this._instantiateMethod,
|
||||
this._protoRecords, this._directiveRecords);
|
||||
PregenProtoChangeDetector._(this.id, this._instantiateMethod);
|
||||
|
||||
static bool isSupported() => true;
|
||||
|
||||
factory PregenProtoChangeDetector(
|
||||
InstantiateMethod instantiateMethod, ChangeDetectorDefinition def) {
|
||||
// TODO(kegluneq): Pre-generate these (#2067).
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
def.bindingRecords.forEach((b) {
|
||||
recordBuilder.add(b, def.variableNames);
|
||||
});
|
||||
var protoRecords = coalesce(recordBuilder.records);
|
||||
return new PregenProtoChangeDetector._(
|
||||
def.id, instantiateMethod, protoRecords, def.directiveRecords);
|
||||
return new PregenProtoChangeDetector._(def.id, instantiateMethod);
|
||||
}
|
||||
|
||||
@override
|
||||
instantiate(dynamic dispatcher) =>
|
||||
_instantiateMethod(dispatcher, _protoRecords, _directiveRecords);
|
||||
instantiate(dynamic dispatcher) => _instantiateMethod(dispatcher);
|
||||
}
|
||||
|
||||
/// Provided as an optimization to cut down on '!' characters in generated
|
||||
|
|
|
@ -29,7 +29,7 @@ import {
|
|||
import {ChangeDetector, ProtoChangeDetector, ChangeDetectorDefinition} from './interfaces';
|
||||
import {ChangeDetectionUtil} from './change_detection_util';
|
||||
import {DynamicChangeDetector} from './dynamic_change_detector';
|
||||
import {BindingRecord} from './binding_record';
|
||||
import {BindingRecord, BindingTarget} from './binding_record';
|
||||
import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
import {EventBinding} from './event_binding';
|
||||
|
||||
|
@ -38,24 +38,30 @@ import {ProtoRecord, RecordType} from './proto_record';
|
|||
|
||||
export class DynamicProtoChangeDetector implements ProtoChangeDetector {
|
||||
_propertyBindingRecords: ProtoRecord[];
|
||||
_propertyBindingTargets: BindingTarget[];
|
||||
_eventBindingRecords: EventBinding[];
|
||||
_directiveIndices: DirectiveIndex[];
|
||||
|
||||
constructor(private definition: ChangeDetectorDefinition) {
|
||||
this._propertyBindingRecords = createPropertyRecords(definition);
|
||||
this._eventBindingRecords = createEventRecords(definition);
|
||||
this._propertyBindingTargets = this.definition.bindingRecords.map(b => b.target);
|
||||
this._directiveIndices = this.definition.directiveRecords.map(d => d.directiveIndex);
|
||||
}
|
||||
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
return new DynamicChangeDetector(this.definition.id, this.definition.strategy, dispatcher,
|
||||
this._propertyBindingRecords, this._eventBindingRecords,
|
||||
this.definition.directiveRecords);
|
||||
return new DynamicChangeDetector(
|
||||
this.definition.id, dispatcher, this._propertyBindingRecords.length,
|
||||
this._propertyBindingTargets, this._directiveIndices,
|
||||
ChangeDetectionUtil.changeDetectionMode(this.definition.strategy),
|
||||
this._propertyBindingRecords, this._eventBindingRecords, this.definition.directiveRecords);
|
||||
}
|
||||
}
|
||||
|
||||
export function createPropertyRecords(definition: ChangeDetectorDefinition): ProtoRecord[] {
|
||||
var recordBuilder = new ProtoRecordBuilder();
|
||||
ListWrapper.forEach(definition.bindingRecords,
|
||||
(b) => { recordBuilder.add(b, definition.variableNames); });
|
||||
ListWrapper.forEachWithIndex(definition.bindingRecords,
|
||||
(b, index) => recordBuilder.add(b, definition.variableNames, index));
|
||||
return coalesce(recordBuilder.records);
|
||||
}
|
||||
|
||||
|
@ -65,7 +71,7 @@ export function createEventRecords(definition: ChangeDetectorDefinition): EventB
|
|||
return definition.eventRecords.map(er => {
|
||||
var records = _ConvertAstIntoProtoRecords.create(er, varNames);
|
||||
var dirIndex = er.implicitReceiver instanceof DirectiveIndex ? er.implicitReceiver : null;
|
||||
return new EventBinding(er.eventName, er.elementIndex, dirIndex, records);
|
||||
return new EventBinding(er.target.name, er.target.elementIndex, dirIndex, records);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -74,13 +80,13 @@ export class ProtoRecordBuilder {
|
|||
|
||||
constructor() { this.records = []; }
|
||||
|
||||
add(b: BindingRecord, variableNames: List<string> = null) {
|
||||
add(b: BindingRecord, variableNames: string[], bindingIndex: number) {
|
||||
var oldLast = ListWrapper.last(this.records);
|
||||
if (isPresent(oldLast) && oldLast.bindingRecord.directiveRecord == b.directiveRecord) {
|
||||
oldLast.lastInDirective = false;
|
||||
}
|
||||
var numberOfRecordsBefore = this.records.length;
|
||||
this._appendRecords(b, variableNames);
|
||||
this._appendRecords(b, variableNames, bindingIndex);
|
||||
var newLast = ListWrapper.last(this.records);
|
||||
if (isPresent(newLast) && newLast !== oldLast) {
|
||||
newLast.lastInBinding = true;
|
||||
|
@ -99,29 +105,30 @@ export class ProtoRecordBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
_appendRecords(b: BindingRecord, variableNames: List<string>) {
|
||||
_appendRecords(b: BindingRecord, variableNames: string[], bindingIndex: number) {
|
||||
if (b.isDirectiveLifecycle()) {
|
||||
this.records.push(new ProtoRecord(RecordType.DIRECTIVE_LIFECYCLE, b.lifecycleEvent, null, [],
|
||||
[], -1, null, this.records.length + 1, b, null, false,
|
||||
false, false, false));
|
||||
[], -1, null, this.records.length + 1, b, false, false,
|
||||
false, false, null));
|
||||
} else {
|
||||
_ConvertAstIntoProtoRecords.append(this.records, b, variableNames);
|
||||
_ConvertAstIntoProtoRecords.append(this.records, b, variableNames, bindingIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _ConvertAstIntoProtoRecords implements AstVisitor {
|
||||
constructor(private _records: List<ProtoRecord>, private _bindingRecord: BindingRecord,
|
||||
private _expressionAsString: string, private _variableNames: List<any>) {}
|
||||
private _variableNames: string[], private _bindingIndex: number) {}
|
||||
|
||||
static append(records: List<ProtoRecord>, b: BindingRecord, variableNames: List<any>) {
|
||||
var c = new _ConvertAstIntoProtoRecords(records, b, b.ast.toString(), variableNames);
|
||||
static append(records: List<ProtoRecord>, b: BindingRecord, variableNames: string[],
|
||||
bindingIndex: number) {
|
||||
var c = new _ConvertAstIntoProtoRecords(records, b, variableNames, bindingIndex);
|
||||
b.ast.visit(c);
|
||||
}
|
||||
|
||||
static create(b: BindingRecord, variableNames: List<any>): ProtoRecord[] {
|
||||
var rec = [];
|
||||
_ConvertAstIntoProtoRecords.append(rec, b, variableNames);
|
||||
_ConvertAstIntoProtoRecords.append(rec, b, variableNames, null);
|
||||
rec[rec.length - 1].lastInBinding = true;
|
||||
return rec;
|
||||
}
|
||||
|
@ -261,12 +268,12 @@ class _ConvertAstIntoProtoRecords implements AstVisitor {
|
|||
var selfIndex = this._records.length + 1;
|
||||
if (context instanceof DirectiveIndex) {
|
||||
this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, -1, context,
|
||||
selfIndex, this._bindingRecord, this._expressionAsString,
|
||||
false, false, false, false));
|
||||
selfIndex, this._bindingRecord, false, false, false, false,
|
||||
this._bindingIndex));
|
||||
} else {
|
||||
this._records.push(new ProtoRecord(type, name, funcOrValue, args, fixedArgs, context, null,
|
||||
selfIndex, this._bindingRecord, this._expressionAsString,
|
||||
false, false, false, false));
|
||||
selfIndex, this._bindingRecord, false, false, false, false,
|
||||
this._bindingIndex));
|
||||
}
|
||||
return selfIndex;
|
||||
}
|
||||
|
|
|
@ -26,9 +26,9 @@ export class ProtoRecord {
|
|||
constructor(public mode: RecordType, public name: string, public funcOrValue,
|
||||
public args: List<any>, public fixedArgs: List<any>, public contextIndex: number,
|
||||
public directiveIndex: DirectiveIndex, public selfIndex: number,
|
||||
public bindingRecord: BindingRecord, public expressionAsString: string,
|
||||
public lastInBinding: boolean, public lastInDirective: boolean,
|
||||
public argumentToPureFunction: boolean, public referencedBySelf: boolean) {}
|
||||
public bindingRecord: BindingRecord, public lastInBinding: boolean,
|
||||
public lastInDirective: boolean, public argumentToPureFunction: boolean,
|
||||
public referencedBySelf: boolean, public propertyBindingIndex: number) {}
|
||||
|
||||
isPureFunction(): boolean {
|
||||
return this.mode === RecordType.INTERPOLATE || this.mode === RecordType.COLLECTION_LITERAL;
|
||||
|
|
|
@ -211,12 +211,10 @@ export class ProtoViewFactory {
|
|||
var nestedPvVariableBindings = _collectNestedProtoViewsVariableBindings(nestedPvsWithIndex);
|
||||
var nestedPvVariableNames = _collectNestedProtoViewsVariableNames(nestedPvsWithIndex);
|
||||
|
||||
var changeDetectorDefs =
|
||||
_getChangeDetectorDefinitions(hostComponentBinding.metadata, nestedPvsWithIndex,
|
||||
var protoChangeDetectors =
|
||||
this._getProtoChangeDetectors(hostComponentBinding, nestedPvsWithIndex,
|
||||
nestedPvVariableNames, allRenderDirectiveMetadata);
|
||||
var protoChangeDetectors = ListWrapper.map(
|
||||
changeDetectorDefs,
|
||||
changeDetectorDef => this._changeDetection.createProtoChangeDetector(changeDetectorDef));
|
||||
|
||||
var appProtoViews = ListWrapper.createFixedSize(nestedPvsWithIndex.length);
|
||||
ListWrapper.forEach(nestedPvsWithIndex, (pvWithIndex: RenderProtoViewWithIndex) => {
|
||||
var appProtoView =
|
||||
|
@ -230,6 +228,24 @@ export class ProtoViewFactory {
|
|||
});
|
||||
return appProtoViews;
|
||||
}
|
||||
|
||||
private _getProtoChangeDetectors(hostComponentBinding: DirectiveBinding,
|
||||
nestedPvsWithIndex: RenderProtoViewWithIndex[],
|
||||
nestedPvVariableNames: string[][],
|
||||
allRenderDirectiveMetadata: any[]): ProtoChangeDetector[] {
|
||||
if (this._changeDetection.generateDetectors) {
|
||||
var changeDetectorDefs =
|
||||
_getChangeDetectorDefinitions(hostComponentBinding.metadata, nestedPvsWithIndex,
|
||||
nestedPvVariableNames, allRenderDirectiveMetadata);
|
||||
return changeDetectorDefs.map(changeDetectorDef =>
|
||||
this._changeDetection.getProtoChangeDetector(
|
||||
changeDetectorDef.id, changeDetectorDef));
|
||||
} else {
|
||||
var changeDetectorIds =
|
||||
_getChangeDetectorDefinitionIds(hostComponentBinding.metadata, nestedPvsWithIndex);
|
||||
return changeDetectorIds.map(id => this._changeDetection.getProtoChangeDetector(id, null));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -280,22 +296,35 @@ function _getChangeDetectorDefinitions(
|
|||
var directiveRecords =
|
||||
bindingRecordsCreator.getDirectiveRecords(elementBinders, allRenderDirectiveMetadata);
|
||||
var strategyName = DEFAULT;
|
||||
var typeString;
|
||||
if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) {
|
||||
strategyName = hostComponentMetadata.changeDetection;
|
||||
typeString = 'comp';
|
||||
} else if (pvWithIndex.renderProtoView.type === ViewType.HOST) {
|
||||
typeString = 'host';
|
||||
} else {
|
||||
typeString = 'embedded';
|
||||
}
|
||||
var id = `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
|
||||
var id = _protoViewId(hostComponentMetadata, pvWithIndex);
|
||||
var variableNames = nestedPvVariableNames[pvWithIndex.index];
|
||||
return new ChangeDetectorDefinition(id, strategyName, variableNames, propBindingRecords,
|
||||
eventBindingRecords, directiveRecords, assertionsEnabled());
|
||||
});
|
||||
}
|
||||
|
||||
function _getChangeDetectorDefinitionIds(hostComponentMetadata: RenderDirectiveMetadata,
|
||||
nestedPvsWithIndex: List<RenderProtoViewWithIndex>):
|
||||
string[] {
|
||||
return nestedPvsWithIndex.map(pvWithIndex => _protoViewId(hostComponentMetadata, pvWithIndex));
|
||||
}
|
||||
|
||||
function _protoViewId(hostComponentMetadata: RenderDirectiveMetadata,
|
||||
pvWithIndex: RenderProtoViewWithIndex): string {
|
||||
var typeString;
|
||||
if (pvWithIndex.renderProtoView.type === ViewType.COMPONENT) {
|
||||
typeString = 'comp';
|
||||
} else if (pvWithIndex.renderProtoView.type === ViewType.HOST) {
|
||||
typeString = 'host';
|
||||
} else {
|
||||
typeString = 'embedded';
|
||||
}
|
||||
return `${hostComponentMetadata.id}_${typeString}_${pvWithIndex.index}`;
|
||||
}
|
||||
|
||||
function _createAppProtoView(
|
||||
renderProtoView: ProtoViewDto, protoChangeDetector: ProtoChangeDetector,
|
||||
variableBindings: Map<string, string>, allDirectives: List<DirectiveBinding>,
|
||||
|
|
|
@ -8,12 +8,12 @@ import {
|
|||
} from 'angular2/src/facade/collection';
|
||||
import {
|
||||
AST,
|
||||
BindingRecord,
|
||||
ChangeDetector,
|
||||
ChangeDetectorRef,
|
||||
ChangeDispatcher,
|
||||
DirectiveIndex,
|
||||
DirectiveRecord,
|
||||
BindingTarget,
|
||||
Locals,
|
||||
ProtoChangeDetector
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
|
@ -171,7 +171,7 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
|||
}
|
||||
|
||||
// dispatch to element injector or text nodes based on context
|
||||
notifyOnBinding(b: BindingRecord, currentValue: any): void {
|
||||
notifyOnBinding(b: BindingTarget, currentValue: any): void {
|
||||
if (b.isTextNode()) {
|
||||
this.renderer.setText(
|
||||
this.render, this.mainMergeMapping.renderTextIndices[b.elementIndex + this.textOffset],
|
||||
|
@ -179,14 +179,14 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
|
|||
} else {
|
||||
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
|
||||
if (b.isElementProperty()) {
|
||||
this.renderer.setElementProperty(elementRef, b.propertyName, currentValue);
|
||||
this.renderer.setElementProperty(elementRef, b.name, currentValue);
|
||||
} else if (b.isElementAttribute()) {
|
||||
this.renderer.setElementAttribute(elementRef, b.propertyName, currentValue);
|
||||
this.renderer.setElementAttribute(elementRef, b.name, currentValue);
|
||||
} else if (b.isElementClass()) {
|
||||
this.renderer.setElementClass(elementRef, b.propertyName, currentValue);
|
||||
this.renderer.setElementClass(elementRef, b.name, currentValue);
|
||||
} else if (b.isElementStyle()) {
|
||||
var unit = isPresent(b.propertyUnit) ? b.propertyUnit : '';
|
||||
this.renderer.setElementStyle(elementRef, b.propertyName, `${currentValue}${unit}`);
|
||||
var unit = isPresent(b.unit) ? b.unit : '';
|
||||
this.renderer.setElementStyle(elementRef, b.name, `${currentValue}${unit}`);
|
||||
} else {
|
||||
throw new BaseException('Unsupported directive record');
|
||||
}
|
||||
|
|
|
@ -134,6 +134,12 @@ class ListWrapper {
|
|||
list.forEach(fn);
|
||||
}
|
||||
|
||||
static void forEachWithIndex(List list, fn(item, index)) {
|
||||
for (var i = 0; i < list.length; ++i) {
|
||||
fn(list[i], i);
|
||||
}
|
||||
}
|
||||
|
||||
static reduce(List list, fn(a, b), init) {
|
||||
return list.fold(init, fn);
|
||||
}
|
||||
|
|
|
@ -182,6 +182,11 @@ export class ListWrapper {
|
|||
fn(array[i]);
|
||||
}
|
||||
}
|
||||
static forEachWithIndex<T>(array: List<T>, fn: (T, number) => void) {
|
||||
for (var i = 0; i < array.length; i++) {
|
||||
fn(array[i], i);
|
||||
}
|
||||
}
|
||||
static first<T>(array: List<T>): T {
|
||||
if (!array) return null;
|
||||
return array[0];
|
||||
|
|
|
@ -9,6 +9,7 @@ import 'package:angular2/src/change_detection/interfaces.dart';
|
|||
import 'package:angular2/src/change_detection/proto_change_detector.dart';
|
||||
import 'package:angular2/src/change_detection/proto_record.dart';
|
||||
import 'package:angular2/src/change_detection/event_binding.dart';
|
||||
import 'package:angular2/src/change_detection/binding_record.dart';
|
||||
import 'package:angular2/src/facade/lang.dart' show BaseException;
|
||||
|
||||
/// Responsible for generating change detector classes for Angular 2.
|
||||
|
@ -79,7 +80,8 @@ class _CodegenState {
|
|||
final List<EventBinding> _eventBindings;
|
||||
final CodegenLogicUtil _logic;
|
||||
final CodegenNameUtil _names;
|
||||
final bool _generateCheckNoChanges;
|
||||
final bool _devMode;
|
||||
final List<BindingTarget> _propertyBindingTargets;
|
||||
|
||||
_CodegenState._(
|
||||
this._changeDetectorDefId,
|
||||
|
@ -87,11 +89,12 @@ class _CodegenState {
|
|||
this._changeDetectorTypeName,
|
||||
String changeDetectionStrategy,
|
||||
this._records,
|
||||
this._propertyBindingTargets,
|
||||
this._eventBindings,
|
||||
this._directiveRecords,
|
||||
this._logic,
|
||||
this._names,
|
||||
this._generateCheckNoChanges)
|
||||
this._devMode)
|
||||
: _changeDetectionMode =
|
||||
ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy);
|
||||
|
||||
|
@ -99,6 +102,8 @@ class _CodegenState {
|
|||
ChangeDetectorDefinition def) {
|
||||
var protoRecords = createPropertyRecords(def);
|
||||
var eventBindings = createEventRecords(def);
|
||||
var propertyBindingTargets = def.bindingRecords.map((b) => b.target).toList();
|
||||
|
||||
var names = new CodegenNameUtil(protoRecords, eventBindings, def.directiveRecords, _UTIL);
|
||||
var logic = new CodegenLogicUtil(names, _UTIL, def.strategy);
|
||||
return new _CodegenState._(
|
||||
|
@ -107,11 +112,12 @@ class _CodegenState {
|
|||
changeDetectorTypeName,
|
||||
def.strategy,
|
||||
protoRecords,
|
||||
propertyBindingTargets,
|
||||
eventBindings,
|
||||
def.directiveRecords,
|
||||
logic,
|
||||
names,
|
||||
def.generateCheckNoChanges);
|
||||
def.devMode);
|
||||
}
|
||||
|
||||
void _writeToBuf(StringBuffer buf) {
|
||||
|
@ -119,9 +125,12 @@ class _CodegenState {
|
|||
class $_changeDetectorTypeName extends $_BASE_CLASS<$_contextTypeName> {
|
||||
${_genDeclareFields()}
|
||||
|
||||
$_changeDetectorTypeName(dispatcher, protos, directiveRecords)
|
||||
$_changeDetectorTypeName(dispatcher)
|
||||
: super(${codify(_changeDetectorDefId)},
|
||||
dispatcher, protos, directiveRecords, '$_changeDetectionMode') {
|
||||
dispatcher, ${_records.length},
|
||||
${_changeDetectorTypeName}.gen_propertyBindingTargets,
|
||||
${_changeDetectorTypeName}.gen_directiveIndices,
|
||||
'$_changeDetectionMode') {
|
||||
dehydrateDirectives(false);
|
||||
}
|
||||
|
||||
|
@ -145,17 +154,31 @@ class _CodegenState {
|
|||
|
||||
${_maybeGenDehydrateDirectives()}
|
||||
|
||||
${_genPropertyBindingTargets()};
|
||||
|
||||
${_genDirectiveIndices()};
|
||||
|
||||
static $_GEN_PREFIX.ProtoChangeDetector
|
||||
$PROTO_CHANGE_DETECTOR_FACTORY_METHOD(
|
||||
$_GEN_PREFIX.ChangeDetectorDefinition def) {
|
||||
return new $_GEN_PREFIX.PregenProtoChangeDetector(
|
||||
(a, b, c) => new $_changeDetectorTypeName(a, b, c),
|
||||
(a) => new $_changeDetectorTypeName(a),
|
||||
def);
|
||||
}
|
||||
}
|
||||
''');
|
||||
}
|
||||
|
||||
String _genPropertyBindingTargets() {
|
||||
var targets = _logic.genPropertyBindingTargets(_propertyBindingTargets, this._devMode);
|
||||
return "static var gen_propertyBindingTargets = ${targets}";
|
||||
}
|
||||
|
||||
String _genDirectiveIndices() {
|
||||
var indices = _logic.genDirectiveIndices(_directiveRecords);
|
||||
return "static var gen_directiveIndices = ${indices}";
|
||||
}
|
||||
|
||||
String _maybeGenHandleEventInternal() {
|
||||
if (_eventBindings.length > 0) {
|
||||
var handlers = _eventBindings.map((eb) => _genEventBinding(eb)).join("\n");
|
||||
|
@ -239,7 +262,7 @@ class _CodegenState {
|
|||
var directiveFieldNames = _names.getAllDirectiveNames();
|
||||
for (var i = 0; i < directiveFieldNames.length; ++i) {
|
||||
buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor('
|
||||
'${_names.getDirectivesAccessorName()}[$i].directiveIndex);');
|
||||
'${_names.getDirectivesAccessorName()}[$i]);');
|
||||
}
|
||||
return '$buf';
|
||||
}
|
||||
|
@ -378,9 +401,9 @@ class _CodegenState {
|
|||
var oldValue = _names.getFieldName(r.selfIndex);
|
||||
|
||||
var br = r.bindingRecord;
|
||||
if (br.isDirective()) {
|
||||
if (br.target.isDirective()) {
|
||||
var directiveProperty =
|
||||
'${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.propertyName}';
|
||||
'${_names.getDirectiveName(br.directiveRecord.directiveIndex)}.${br.target.name}';
|
||||
return '''
|
||||
${_genThrowOnChangeCheck(oldValue, newValue)}
|
||||
$directiveProperty = $newValue;
|
||||
|
@ -395,7 +418,7 @@ class _CodegenState {
|
|||
}
|
||||
|
||||
String _genThrowOnChangeCheck(String oldValue, String newValue) {
|
||||
if (this._generateCheckNoChanges) {
|
||||
if (this._devMode) {
|
||||
return '''
|
||||
if(throwOnChange) {
|
||||
this.throwOnChangeError(${oldValue}, ${newValue});
|
||||
|
@ -407,7 +430,7 @@ class _CodegenState {
|
|||
}
|
||||
|
||||
String _genCheckNoChanges() {
|
||||
if (this._generateCheckNoChanges) {
|
||||
if (this._devMode) {
|
||||
return 'void checkNoChanges() { runDetectChanges(true); }';
|
||||
} else {
|
||||
return '';
|
||||
|
@ -417,8 +440,8 @@ class _CodegenState {
|
|||
String _maybeFirstInBinding(ProtoRecord r) {
|
||||
var prev = ChangeDetectionUtil.protoByIndex(_records, r.selfIndex - 1);
|
||||
var firstInBindng = prev == null || prev.bindingRecord != r.bindingRecord;
|
||||
return firstInBindng
|
||||
? "${_names.getFirstProtoInCurrentBinding()} = ${r.selfIndex};"
|
||||
return firstInBindng && !r.bindingRecord.isDirectiveLifecycle()
|
||||
? "${_names.getPropertyBindingIndex()} = ${r.propertyBindingIndex};"
|
||||
: '';
|
||||
}
|
||||
|
||||
|
|
|
@ -14,7 +14,7 @@ class NullReflectionCapabilities implements ReflectionCapabilities {
|
|||
return false;
|
||||
}
|
||||
|
||||
Function factory(Type type) => _notImplemented('factory');
|
||||
Function factory(Type type) => _notImplemented("factory");
|
||||
|
||||
List<List> parameters(typeOrFunc) => _notImplemented('parameters');
|
||||
|
||||
|
|
|
@ -30,12 +30,12 @@ export function main() {
|
|||
var map = {'id': (def) => proto};
|
||||
var cd = new PreGeneratedChangeDetection(map);
|
||||
|
||||
expect(cd.createProtoChangeDetector(def)).toBe(proto)
|
||||
expect(cd.getProtoChangeDetector('id', def)).toBe(proto)
|
||||
});
|
||||
|
||||
it("should delegate to dynamic change detection otherwise", () => {
|
||||
var cd = new PreGeneratedChangeDetection({});
|
||||
expect(cd.createProtoChangeDetector(def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
|
||||
expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1135,8 +1135,8 @@ class TestDispatcher implements ChangeDispatcher {
|
|||
this.onAllChangesDoneCalled = true;
|
||||
}
|
||||
|
||||
notifyOnBinding(binding, value) {
|
||||
this.log.push(`${binding.propertyName}=${this._asString(value)}`);
|
||||
notifyOnBinding(target, value) {
|
||||
this.log.push(`${target.name}=${this._asString(value)}`);
|
||||
this.loggedValues.push(value);
|
||||
}
|
||||
|
||||
|
|
|
@ -21,8 +21,7 @@ export function main() {
|
|||
if (isBlank(argumentToPureFunction)) argumentToPureFunction = false;
|
||||
|
||||
return new ProtoRecord(mode, name, funcOrValue, args, null, contextIndex, directiveIndex,
|
||||
selfIndex, null, null, lastInBinding, false, argumentToPureFunction,
|
||||
false);
|
||||
selfIndex, null, lastInBinding, false, argumentToPureFunction, false, 0);
|
||||
}
|
||||
|
||||
describe("change detection - coalesce", () => {
|
||||
|
@ -64,7 +63,7 @@ export function main() {
|
|||
[r("user", [], 0, 1, {lastInBinding: true}), r("user", [], 0, 2, {lastInBinding: true})]);
|
||||
|
||||
expect(rs[1]).toEqual(new ProtoRecord(RecordType.SELF, "self", null, [], null, 1, null, 2,
|
||||
null, null, true, false, false, false));
|
||||
null, true, false, false, false, 0));
|
||||
});
|
||||
|
||||
it("should set referencedBySelf", () => {
|
||||
|
|
|
@ -19,7 +19,7 @@ export function main() {
|
|||
it('should set argumentToPureFunction flag', inject([Parser], (p: Parser) => {
|
||||
var builder = new ProtoRecordBuilder();
|
||||
var ast = p.parseBinding("[1,2]", "location"); // collection literal is a pure function
|
||||
builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), []);
|
||||
builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), [], 0);
|
||||
|
||||
var isPureFunc = builder.records.map(r => r.argumentToPureFunction);
|
||||
expect(isPureFunc).toEqual([true, true, false]);
|
||||
|
@ -29,7 +29,7 @@ export function main() {
|
|||
inject([Parser], (p: Parser) => {
|
||||
var builder = new ProtoRecordBuilder();
|
||||
var ast = p.parseBinding("f(1,2)", "location");
|
||||
builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), []);
|
||||
builder.add(BindingRecord.createForElementProperty(ast, 0, "property"), [], 0);
|
||||
|
||||
var isPureFunc = builder.records.map(r => r.argumentToPureFunction);
|
||||
expect(isPureFunc).toEqual([false, false, false]);
|
||||
|
|
|
@ -20,8 +20,8 @@ export function main() {
|
|||
if (isBlank(argumentToPureFunction)) argumentToPureFunction = false;
|
||||
if (isBlank(referencedBySelf)) referencedBySelf = false;
|
||||
|
||||
return new ProtoRecord(mode, name, null, [], null, 0, directiveIndex, 0, null, null,
|
||||
lastInBinding, false, argumentToPureFunction, referencedBySelf);
|
||||
return new ProtoRecord(mode, name, null, [], null, 0, directiveIndex, 0, null, lastInBinding,
|
||||
false, argumentToPureFunction, referencedBySelf, 0);
|
||||
}
|
||||
|
||||
describe("ProtoRecord", () => {
|
||||
|
|
|
@ -980,7 +980,7 @@ export function main() {
|
|||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the component's view into the component", () => {
|
||||
var cd = new DynamicChangeDetector(null, null, null, [], [], []);
|
||||
var cd = new DynamicChangeDetector(null, null, 0, [], [], null, [], [], []);
|
||||
var view = <any>new DummyView();
|
||||
var childView = new DummyView();
|
||||
childView.changeDetector = cd;
|
||||
|
@ -993,7 +993,7 @@ export function main() {
|
|||
});
|
||||
|
||||
it("should inject ChangeDetectorRef of the containing component into directives", () => {
|
||||
var cd = new DynamicChangeDetector(null, null, null, [], [], []);
|
||||
var cd = new DynamicChangeDetector(null, null, 0, [], [], null, [], [], []);
|
||||
var view = <any>new DummyView();
|
||||
view.changeDetector =cd;
|
||||
var binding = DirectiveBinding.createFromType(DirectiveNeedsChangeDetectorRef, new DirectiveMetadata());
|
||||
|
|
|
@ -20,7 +20,8 @@ import {
|
|||
ChangeDetection,
|
||||
ChangeDetectorDefinition,
|
||||
BindingRecord,
|
||||
DirectiveIndex
|
||||
DirectiveIndex,
|
||||
Parser
|
||||
} from 'angular2/src/change_detection/change_detection';
|
||||
import {
|
||||
BindingRecordsCreator,
|
||||
|
@ -177,39 +178,44 @@ export function main() {
|
|||
beforeEach(() => { creator = new BindingRecordsCreator(); });
|
||||
|
||||
describe('getEventBindingRecords', () => {
|
||||
it("should return template event records", () => {
|
||||
var rec = creator.getEventBindingRecords(
|
||||
[
|
||||
new RenderElementBinder(
|
||||
{eventBindings: [new EventBinding("a", null)], directives: []}),
|
||||
new RenderElementBinder(
|
||||
{eventBindings: [new EventBinding("b", null)], directives: []})
|
||||
],
|
||||
[]);
|
||||
it("should return template event records", inject([Parser], (p: Parser) => {
|
||||
var ast1 = p.parseAction("1", null);
|
||||
var ast2 = p.parseAction("2", null);
|
||||
|
||||
expect(rec).toEqual([
|
||||
BindingRecord.createForEvent(null, "a", 0),
|
||||
BindingRecord.createForEvent(null, "b", 1)
|
||||
]);
|
||||
});
|
||||
var rec = creator.getEventBindingRecords(
|
||||
[
|
||||
new RenderElementBinder(
|
||||
{eventBindings: [new EventBinding("a", ast1)], directives: []}),
|
||||
new RenderElementBinder(
|
||||
{eventBindings: [new EventBinding("b", ast2)], directives: []})
|
||||
],
|
||||
[]);
|
||||
|
||||
it('should return host event records', () => {
|
||||
var rec = creator.getEventBindingRecords(
|
||||
[
|
||||
new RenderElementBinder({
|
||||
eventBindings: [],
|
||||
directives: [
|
||||
new DirectiveBinder(
|
||||
{directiveIndex: 0, eventBindings: [new EventBinding("a", null)]})
|
||||
]
|
||||
})
|
||||
],
|
||||
[RenderDirectiveMetadata.create({id: 'some-id'})]);
|
||||
expect(rec).toEqual([
|
||||
BindingRecord.createForEvent(ast1, "a", 0),
|
||||
BindingRecord.createForEvent(ast2, "b", 1)
|
||||
]);
|
||||
}));
|
||||
|
||||
expect(rec.length).toEqual(1);
|
||||
expect(rec[0].eventName).toEqual("a");
|
||||
expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex);
|
||||
});
|
||||
it('should return host event records', inject([Parser], (p: Parser) => {
|
||||
var ast1 = p.parseAction("1", null);
|
||||
|
||||
var rec = creator.getEventBindingRecords(
|
||||
[
|
||||
new RenderElementBinder({
|
||||
eventBindings: [],
|
||||
directives: [
|
||||
new DirectiveBinder(
|
||||
{directiveIndex: 0, eventBindings: [new EventBinding("a", ast1)]})
|
||||
]
|
||||
})
|
||||
],
|
||||
[RenderDirectiveMetadata.create({id: 'some-id'})]);
|
||||
|
||||
expect(rec.length).toEqual(1);
|
||||
expect(rec[0].target.name).toEqual("a");
|
||||
expect(rec[0].implicitReceiver).toBeAnInstanceOf(DirectiveIndex);
|
||||
}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -253,6 +259,7 @@ function createRenderViewportElementBinder(nestedProtoView) {
|
|||
class ChangeDetectionSpy extends SpyObject {
|
||||
constructor() { super(ChangeDetection); }
|
||||
noSuchMethod(m) { return super.noSuchMethod(m) }
|
||||
get generateDetectors() { return true; }
|
||||
}
|
||||
|
||||
@Component({selector: 'main-comp'})
|
||||
|
|
|
@ -90,6 +90,18 @@ export function main() {
|
|||
it('should return null for an empty list',
|
||||
() => { expect(ListWrapper.maximum([], x => x)).toEqual(null); });
|
||||
});
|
||||
|
||||
describe('forEachWithIndex', () => {
|
||||
var l;
|
||||
|
||||
beforeEach(() => { l = ["a", "b"]; });
|
||||
|
||||
it('should iterate over an array passing values and indices', () => {
|
||||
var record = [];
|
||||
ListWrapper.forEachWithIndex(l, (value, index) => record.push([value, index]));
|
||||
expect(record).toEqual([["a", 0], ["b", 1]]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('StringMapWrapper', () => {
|
||||
|
|
|
@ -13,12 +13,10 @@ void initReflector() {
|
|||
if (_visited) return;
|
||||
_visited = true;
|
||||
_ngRef.reflector
|
||||
..registerType(
|
||||
MyComponent,
|
||||
new _ngRef.ReflectionInfo(const [
|
||||
const Component(selector: '[soup]'),
|
||||
const View(template: 'Salad: {{myNum}} is awesome')
|
||||
], const [], () => new MyComponent()))
|
||||
..registerType(MyComponent, new _ngRef.ReflectionInfo(const [
|
||||
const Component(selector: '[soup]'),
|
||||
const View(template: 'Salad: {{myNum}} is awesome')
|
||||
], const [], () => new MyComponent()))
|
||||
..registerGetters({'myNum': (o) => o.myNum});
|
||||
_gen.preGeneratedProtoDetectors['MyComponent_comp_0'] =
|
||||
_MyComponent_ChangeDetector0.newProtoChangeDetector;
|
||||
|
@ -28,19 +26,23 @@ class _MyComponent_ChangeDetector0
|
|||
extends _gen.AbstractChangeDetector<MyComponent> {
|
||||
var myNum0, interpolate1;
|
||||
|
||||
_MyComponent_ChangeDetector0(dispatcher, protos, directiveRecords)
|
||||
: super("MyComponent_comp_0", dispatcher, protos, directiveRecords,
|
||||
'ALWAYS_CHECK') {
|
||||
_MyComponent_ChangeDetector0(dispatcher) : super(
|
||||
"MyComponent_comp_0", dispatcher, 2,
|
||||
_MyComponent_ChangeDetector0.gen_propertyBindingTargets,
|
||||
_MyComponent_ChangeDetector0.gen_directiveIndices, 'ALWAYS_CHECK') {
|
||||
dehydrateDirectives(false);
|
||||
}
|
||||
|
||||
void detectChangesInRecordsInternal(throwOnChange) {
|
||||
var l_context = this.context, l_myNum0, c_myNum0, l_interpolate1;
|
||||
var l_context = this.context,
|
||||
l_myNum0,
|
||||
c_myNum0,
|
||||
l_interpolate1;
|
||||
c_myNum0 = false;
|
||||
var isChanged = false;
|
||||
var changes = null;
|
||||
|
||||
this.firstProtoInCurrentBinding = 1;
|
||||
this.propertyBindingIndex = 0;
|
||||
l_myNum0 = l_context.myNum;
|
||||
if (_gen.looseNotIdentical(l_myNum0, this.myNum0)) {
|
||||
c_myNum0 = true;
|
||||
|
@ -75,9 +77,16 @@ class _MyComponent_ChangeDetector0
|
|||
this.myNum0 = this.interpolate1 = _gen.ChangeDetectionUtil.uninitialized;
|
||||
}
|
||||
|
||||
static var gen_propertyBindingTargets = [
|
||||
_gen.ChangeDetectionUtil.bindingTarget("textNode", 0, null, null,
|
||||
"Salad: {{myNum}} is awesome in MyComponent: <template>")
|
||||
];
|
||||
|
||||
static var gen_directiveIndices = [];
|
||||
|
||||
static _gen.ProtoChangeDetector newProtoChangeDetector(
|
||||
_gen.ChangeDetectorDefinition def) {
|
||||
return new _gen.PregenProtoChangeDetector(
|
||||
(a, b, c) => new _MyComponent_ChangeDetector0(a, b, c), def);
|
||||
(a) => new _MyComponent_ChangeDetector0(a), def);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -249,8 +249,8 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
|
|||
var dispatcher = new DummyDispatcher();
|
||||
var parser = new Parser(new Lexer());
|
||||
|
||||
var parentProto = changeDetection.createProtoChangeDetector(
|
||||
new ChangeDetectorDefinition('parent', null, [], [], [], [], false));
|
||||
var parentProto = changeDetection.getProtoChangeDetector(
|
||||
"id", new ChangeDetectorDefinition('parent', null, [], [], [], [], false));
|
||||
var parentCd = parentProto.instantiate(dispatcher);
|
||||
|
||||
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
|
||||
|
@ -277,7 +277,8 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
|
|||
reflector.setter("field9"), directiveRecord)
|
||||
];
|
||||
|
||||
var proto = changeDetection.createProtoChangeDetector(
|
||||
var proto = changeDetection.getProtoChangeDetector(
|
||||
"id",
|
||||
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], false));
|
||||
|
||||
var targetObj = new Obj();
|
||||
|
|
Loading…
Reference in New Issue