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

View File

@ -28,4 +28,5 @@ export {
KeyValueDiffers, KeyValueDiffers,
KeyValueDiffer, KeyValueDiffer,
KeyValueDifferFactory KeyValueDifferFactory
} from 'angular2/src/change_detection/change_detection'; } from 'angular2/src/change_detection/change_detection';

View File

@ -17,6 +17,5 @@ export {
ViewDefinition, ViewDefinition,
DOCUMENT, DOCUMENT,
APP_ID, APP_ID,
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE
} from './src/render/render'; } from './src/render/render';

View File

@ -211,6 +211,10 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
this.dispatcher.notifyOnBinding(this._currentBinding(), value); this.dispatcher.notifyOnBinding(this._currentBinding(), value);
} }
protected logBindingUpdate(value: any): void {
this.dispatcher.logBindingUpdate(this._currentBinding(), value);
}
protected addChange(changes: StringMap<string, any>, oldValue: any, protected addChange(changes: StringMap<string, any>, oldValue: any,
newValue: any): StringMap<string, any> { newValue: any): StringMap<string, any> {
if (isBlank(changes)) { if (isBlank(changes)) {

View File

@ -64,7 +64,8 @@ export class BindingRecord {
static createForDirective(ast: AST, propertyName: string, setter: SetterFn, static createForDirective(ast: AST, propertyName: string, setter: SetterFn,
directiveRecord: DirectiveRecord): BindingRecord { directiveRecord: DirectiveRecord): BindingRecord {
var t = new BindingTarget(DIRECTIVE, null, propertyName, null, ast.toString()); var elementIndex = directiveRecord.directiveIndex.elementIndex;
var t = new BindingTarget(DIRECTIVE, elementIndex, propertyName, null, ast.toString());
return new BindingRecord(DIRECTIVE, t, 0, ast, setter, null, directiveRecord); return new BindingRecord(DIRECTIVE, t, 0, ast, setter, null, directiveRecord);
} }

View File

@ -84,8 +84,6 @@ export const defaultKeyValueDiffers = CONST_EXPR(new KeyValueDiffers(keyValDiff)
// dart2js. See https://github.com/dart-lang/sdk/issues/23630 for details. // dart2js. See https://github.com/dart-lang/sdk/issues/23630 for details.
export var preGeneratedProtoDetectors: StringMap<string, Function> = {}; export var preGeneratedProtoDetectors: StringMap<string, Function> = {};
export const PROTO_CHANGE_DETECTOR = CONST_EXPR(new OpaqueToken('ProtoChangeDetectors'));
/** /**
* Implements change detection using a map of pregenerated proto detectors. * Implements change detection using a map of pregenerated proto detectors.
*/ */
@ -93,14 +91,19 @@ export const PROTO_CHANGE_DETECTOR = CONST_EXPR(new OpaqueToken('ProtoChangeDete
export class PreGeneratedChangeDetection extends ChangeDetection { export class PreGeneratedChangeDetection extends ChangeDetection {
_dynamicChangeDetection: ChangeDetection; _dynamicChangeDetection: ChangeDetection;
_protoChangeDetectorFactories: StringMap<string, Function>; _protoChangeDetectorFactories: StringMap<string, Function>;
_genConfig: ChangeDetectorGenConfig;
constructor(@Inject(PROTO_CHANGE_DETECTOR) @Optional() protoChangeDetectorsForTest?: constructor(config?: ChangeDetectorGenConfig,
StringMap<string, Function>) { protoChangeDetectorsForTest?: StringMap<string, Function>) {
super(); super();
this._dynamicChangeDetection = new DynamicChangeDetection(); this._dynamicChangeDetection = new DynamicChangeDetection();
this._protoChangeDetectorFactories = isPresent(protoChangeDetectorsForTest) ? this._protoChangeDetectorFactories = isPresent(protoChangeDetectorsForTest) ?
protoChangeDetectorsForTest : protoChangeDetectorsForTest :
preGeneratedProtoDetectors; preGeneratedProtoDetectors;
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false);
} }
static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); } static isSupported(): boolean { return PregenProtoChangeDetector.isSupported(); }
@ -112,11 +115,8 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
return this._dynamicChangeDetection.getProtoChangeDetector(id, definition); return this._dynamicChangeDetection.getProtoChangeDetector(id, definition);
} }
get genConfig(): ChangeDetectorGenConfig { return this._genConfig; }
get generateDetectors(): boolean { return true; } get generateDetectors(): boolean { return true; }
get genConfig(): ChangeDetectorGenConfig {
return new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled());
}
} }
@ -127,15 +127,21 @@ export class PreGeneratedChangeDetection extends ChangeDetection {
*/ */
@Injectable() @Injectable()
export class DynamicChangeDetection extends ChangeDetection { export class DynamicChangeDetection extends ChangeDetection {
_genConfig: ChangeDetectorGenConfig;
constructor(config?: ChangeDetectorGenConfig) {
super();
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false);
}
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector { getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new DynamicProtoChangeDetector(definition); return new DynamicProtoChangeDetector(definition);
} }
get genConfig(): ChangeDetectorGenConfig { return this._genConfig; }
get generateDetectors(): boolean { return true; } get generateDetectors(): boolean { return true; }
get genConfig(): ChangeDetectorGenConfig {
return new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled());
}
} }
/** /**
@ -145,17 +151,21 @@ export class DynamicChangeDetection extends ChangeDetection {
* {@link DynamicChangeDetection} and {@link PreGeneratedChangeDetection}. * {@link DynamicChangeDetection} and {@link PreGeneratedChangeDetection}.
*/ */
@Injectable() @Injectable()
@CONST()
export class JitChangeDetection extends ChangeDetection { export class JitChangeDetection extends ChangeDetection {
_genConfig: ChangeDetectorGenConfig;
constructor(config?: ChangeDetectorGenConfig) {
super();
this._genConfig =
isPresent(config) ? config : new ChangeDetectorGenConfig(assertionsEnabled(),
assertionsEnabled(), false);
}
static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); } static isSupported(): boolean { return JitProtoChangeDetector.isSupported(); }
getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector { getProtoChangeDetector(id: string, definition: ChangeDetectorDefinition): ProtoChangeDetector {
return new JitProtoChangeDetector(definition); return new JitProtoChangeDetector(definition);
} }
get genConfig(): ChangeDetectorGenConfig { return this._genConfig; }
get generateDetectors(): boolean { return true; } get generateDetectors(): boolean { return true; }
get genConfig(): ChangeDetectorGenConfig {
return new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled());
}
} }

View File

@ -82,7 +82,6 @@ export class ChangeDetectorJITGenerator {
return new ${this._typeName}(dispatcher); return new ${this._typeName}(dispatcher);
} }
`; `;
return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector, return new Function(ABSTRACT_CHANGE_DETECTOR, UTIL, classDefinition)(AbstractChangeDetector,
ChangeDetectionUtil); ChangeDetectionUtil);
} }
@ -301,6 +300,7 @@ export class ChangeDetectorJITGenerator {
var newValue = this._names.getLocalName(r.selfIndex); var newValue = this._names.getLocalName(r.selfIndex);
var oldValue = this._names.getFieldName(r.selfIndex); var oldValue = this._names.getFieldName(r.selfIndex);
var notifyDebug = this.genConfig.logBindingUpdate ? `this.logBindingUpdate(${newValue});` : "";
var br = r.bindingRecord; var br = r.bindingRecord;
if (br.target.isDirective()) { if (br.target.isDirective()) {
@ -309,12 +309,14 @@ export class ChangeDetectorJITGenerator {
return ` return `
${this._genThrowOnChangeCheck(oldValue, newValue)} ${this._genThrowOnChangeCheck(oldValue, newValue)}
${directiveProperty} = ${newValue}; ${directiveProperty} = ${newValue};
${notifyDebug}
${IS_CHANGED_LOCAL} = true; ${IS_CHANGED_LOCAL} = true;
`; `;
} else { } else {
return ` return `
${this._genThrowOnChangeCheck(oldValue, newValue)} ${this._genThrowOnChangeCheck(oldValue, newValue)}
this.notifyDispatcher(${newValue}); this.notifyDispatcher(${newValue});
${notifyDebug}
`; `;
} }
} }

View File

@ -179,6 +179,10 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
var directiveIndex = bindingRecord.directiveRecord.directiveIndex; var directiveIndex = bindingRecord.directiveRecord.directiveIndex;
bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue); bindingRecord.setter(this._getDirectiveFor(directiveIndex), change.currentValue);
} }
if (this.genConfig.logBindingUpdate) {
super.logBindingUpdate(change.currentValue);
}
} }
_addChange(bindingRecord: BindingRecord, change, changes) { _addChange(bindingRecord: BindingRecord, change, changes) {

View File

@ -27,7 +27,7 @@ import {ChangeDetectorRef} from './change_detector_ref';
* *
* # Example * # Example
* ```javascript * ```javascript
* bootstrap(MyApp, [bind(ChangeDetection).toClass(DynamicChangeDetection)]); * bootstrap(MyApp, [bind(ChangeDetection).toValue(new DynamicChangeDetection())]);
* ``` * ```
*/ */
@CONST() @CONST()
@ -49,6 +49,7 @@ export class DebugContext {
export interface ChangeDispatcher { export interface ChangeDispatcher {
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext; getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext;
notifyOnBinding(bindingTarget: BindingTarget, value: any): void; notifyOnBinding(bindingTarget: BindingTarget, value: any): void;
logBindingUpdate(bindingTarget: BindingTarget, value: any): void;
notifyOnAllChangesDone(): void; notifyOnAllChangesDone(): void;
} }
@ -74,7 +75,8 @@ export interface ChangeDetector {
export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; } export interface ProtoChangeDetector { instantiate(dispatcher: ChangeDispatcher): ChangeDetector; }
export class ChangeDetectorGenConfig { export class ChangeDetectorGenConfig {
constructor(public genCheckNoChanges: boolean, public genDebugInfo: boolean) {} constructor(public genCheckNoChanges: boolean, public genDebugInfo: boolean,
public logBindingUpdate: boolean) {}
} }
export class ChangeDetectorDefinition { export class ChangeDetectorDefinition {

View File

@ -60,7 +60,6 @@ import {Renderer, RenderCompiler} from 'angular2/src/render/api';
import { import {
DomRenderer, DomRenderer,
DOCUMENT, DOCUMENT,
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
DefaultDomCompiler, DefaultDomCompiler,
APP_ID_RANDOM_BINDING, APP_ID_RANDOM_BINDING,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE, MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
@ -84,16 +83,15 @@ var _rootInjector: Injector;
var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry]; var _rootBindings = [bind(Reflector).toValue(reflector), TestabilityRegistry];
function _injectorBindings(appComponentType): List<Type | Binding | List<any>> { function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
var bestChangeDetection: Type = DynamicChangeDetection; var bestChangeDetection = new DynamicChangeDetection();
if (PreGeneratedChangeDetection.isSupported()) { if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = PreGeneratedChangeDetection; bestChangeDetection = new PreGeneratedChangeDetection();
} else if (JitChangeDetection.isSupported()) { } else if (JitChangeDetection.isSupported()) {
bestChangeDetection = JitChangeDetection; bestChangeDetection = new JitChangeDetection();
} }
return [ return [
bind(DOCUMENT) bind(DOCUMENT)
.toValue(DOM.defaultDoc()), .toValue(DOM.defaultDoc()),
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
bind(APP_COMPONENT).toValue(appComponentType), bind(APP_COMPONENT).toValue(appComponentType),
bind(APP_COMPONENT_REF_PROMISE) bind(APP_COMPONENT_REF_PROMISE)
.toFactory( .toFactory(
@ -141,7 +139,7 @@ function _injectorBindings(appComponentType): List<Type | Binding | List<any>> {
DEFAULT_PIPES, DEFAULT_PIPES,
bind(IterableDiffers).toValue(defaultIterableDiffers), bind(IterableDiffers).toValue(defaultIterableDiffers),
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
bind(ChangeDetection).toClass(bestChangeDetection), bind(ChangeDetection).toValue(bestChangeDetection),
ViewLoader, ViewLoader,
DirectiveResolver, DirectiveResolver,
PipeResolver, PipeResolver,

View File

@ -32,9 +32,12 @@ import {RenderEventDispatcher} from 'angular2/src/render/api';
import {ViewRef, ProtoViewRef, internalView} from './view_ref'; import {ViewRef, ProtoViewRef, internalView} from './view_ref';
import {ElementRef} from './element_ref'; import {ElementRef} from './element_ref';
import {ProtoPipes} from 'angular2/src/core/pipes/pipes'; import {ProtoPipes} from 'angular2/src/core/pipes/pipes';
import {camelCaseToDashCase} from 'angular2/src/render/dom/util';
export {DebugContext} from 'angular2/src/change_detection/interfaces'; export {DebugContext} from 'angular2/src/change_detection/interfaces';
const REFLECT_PREFIX: string = 'ng-reflect-';
export class AppProtoViewMergeMapping { export class AppProtoViewMergeMapping {
renderProtoViewRef: renderApi.RenderProtoViewRef; renderProtoViewRef: renderApi.RenderProtoViewRef;
renderFragmentCount: number; renderFragmentCount: number;
@ -193,6 +196,14 @@ export class AppView implements ChangeDispatcher, RenderEventDispatcher {
} }
} }
logBindingUpdate(b: BindingTarget, value: any): void {
if (b.isDirective() || b.isElementProperty()) {
var elementRef = this.elementRefs[this.elementOffset + b.elementIndex];
this.renderer.setElementAttribute(
elementRef, `${REFLECT_PREFIX}${camelCaseToDashCase(b.name)}`, `${value}`);
}
}
notifyOnAllChangesDone(): void { notifyOnAllChangesDone(): void {
var eiCount = this.proto.elementBinders.length; var eiCount = this.proto.elementBinders.length;
var ei = this.elementInjectors; var ei = this.elementInjectors;

View File

@ -72,4 +72,4 @@ export class NgModel extends NgControl {
this.viewModel = newValue; this.viewModel = newValue;
ObservableWrapper.callNext(this.update, newValue); ObservableWrapper.callNext(this.update, newValue);
} }
} }

View File

@ -35,7 +35,7 @@ import {
import {TemplateCloner} from './template_cloner'; import {TemplateCloner} from './template_cloner';
import {DOCUMENT, DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES} from './dom_tokens'; import {DOCUMENT} from './dom_tokens';
const REFLECT_PREFIX: string = 'ng-reflect-'; const REFLECT_PREFIX: string = 'ng-reflect-';
@ -43,15 +43,11 @@ const REFLECT_PREFIX: string = 'ng-reflect-';
@Injectable() @Injectable()
export class DomRenderer extends Renderer { export class DomRenderer extends Renderer {
_document; _document;
_reflectPropertiesAsAttributes: boolean;
constructor(private _eventManager: EventManager, constructor(private _eventManager: EventManager,
private _domSharedStylesHost: DomSharedStylesHost, private _domSharedStylesHost: DomSharedStylesHost,
private _templateCloner: TemplateCloner, @Inject(DOCUMENT) document, private _templateCloner: TemplateCloner, @Inject(DOCUMENT) document) {
@Inject(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES) reflectPropertiesAsAttributes:
boolean) {
super(); super();
this._reflectPropertiesAsAttributes = reflectPropertiesAsAttributes;
this._document = document; this._document = document;
} }
@ -165,11 +161,6 @@ export class DomRenderer extends Renderer {
} }
var view = resolveInternalDomView(location.renderView); var view = resolveInternalDomView(location.renderView);
view.setElementProperty(location.renderBoundElementIndex, propertyName, propertyValue); view.setElementProperty(location.renderBoundElementIndex, propertyName, propertyValue);
// Reflect the property value as an attribute value with ng-reflect- prefix.
if (this._reflectPropertiesAsAttributes) {
this.setElementAttribute(location, `${REFLECT_PREFIX}${camelCaseToDashCase(propertyName)}`,
`${propertyValue}`);
}
} }
setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string): setElementAttribute(location: RenderElementRef, attributeName: string, attributeValue: string):

View File

@ -3,10 +3,6 @@ import {CONST_EXPR, StringWrapper, Math} from 'angular2/src/facade/lang';
export const DOCUMENT: OpaqueToken = CONST_EXPR(new OpaqueToken('DocumentToken')); export const DOCUMENT: OpaqueToken = CONST_EXPR(new OpaqueToken('DocumentToken'));
export const DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES: OpaqueToken =
CONST_EXPR(new OpaqueToken('DomReflectPropertiesAsAttributes'));
/** /**
* A unique id (string) for an angular application. * A unique id (string) for an angular application.
*/ */

View File

@ -54,7 +54,6 @@ import {RenderCompiler, Renderer} from 'angular2/src/render/api';
import { import {
DomRenderer, DomRenderer,
DOCUMENT, DOCUMENT,
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
DefaultDomCompiler, DefaultDomCompiler,
APP_ID, APP_ID,
SharedStylesHost, SharedStylesHost,
@ -111,7 +110,6 @@ function _getAppBindings() {
bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()), bind(ElementSchemaRegistry).toValue(new DomElementSchemaRegistry()),
DomSharedStylesHost, DomSharedStylesHost,
bind(SharedStylesHost).toAlias(DomSharedStylesHost), bind(SharedStylesHost).toAlias(DomSharedStylesHost),
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
ProtoViewFactory, ProtoViewFactory,
AppViewPool, AppViewPool,
AppViewManager, AppViewManager,
@ -125,7 +123,7 @@ function _getAppBindings() {
DEFAULT_PIPES, DEFAULT_PIPES,
bind(IterableDiffers).toValue(defaultIterableDiffers), bind(IterableDiffers).toValue(defaultIterableDiffers),
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
bind(ChangeDetection).toClass(DynamicChangeDetection), bind(ChangeDetection).toValue(new DynamicChangeDetection()),
Log, Log,
ViewLoader, ViewLoader,
DynamicComponentLoader, DynamicComponentLoader,

View File

@ -10,6 +10,7 @@ const CUSTOM_ANNOTATIONS_PARAM = 'custom_annotations';
const ENTRY_POINT_PARAM = 'entry_points'; const ENTRY_POINT_PARAM = 'entry_points';
const FORMAT_CODE_PARAM = 'format_code'; const FORMAT_CODE_PARAM = 'format_code';
const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors'; const GENERATE_CHANGE_DETECTORS_PARAM = 'generate_change_detectors';
const REFLECT_PROPERTIES_AS_ATTRIBUTES = 'reflectPropertiesAsAttributes';
const INIT_REFLECTOR_PARAM = 'init_reflector'; const INIT_REFLECTOR_PARAM = 'init_reflector';
const INLINE_VIEWS_PARAM = 'inline_views'; const INLINE_VIEWS_PARAM = 'inline_views';
const MIRROR_MODE_PARAM = 'mirror_mode'; const MIRROR_MODE_PARAM = 'mirror_mode';
@ -43,6 +44,8 @@ class TransformerOptions {
/// Whether to create change detector classes for discovered `@View`s. /// Whether to create change detector classes for discovered `@View`s.
final bool generateChangeDetectors; final bool generateChangeDetectors;
final bool reflectPropertiesAsAttributes;
/// The number of phases to spend optimizing output size. /// The number of phases to spend optimizing output size.
/// Each additional phase adds time to the transformation but may decrease /// Each additional phase adds time to the transformation but may decrease
/// final output size. There is a limit beyond which this will no longer /// final output size. There is a limit beyond which this will no longer
@ -66,6 +69,7 @@ class TransformerOptions {
this.annotationMatcher, this.annotationMatcher,
this.optimizationPhases, this.optimizationPhases,
{this.generateChangeDetectors, {this.generateChangeDetectors,
this.reflectPropertiesAsAttributes,
this.inlineViews, this.inlineViews,
this.formatCode}); this.formatCode});
@ -78,6 +82,7 @@ class TransformerOptions {
int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES, int optimizationPhases: DEFAULT_OPTIMIZATION_PHASES,
bool inlineViews: true, bool inlineViews: true,
bool generateChangeDetectors: true, bool generateChangeDetectors: true,
bool reflectPropertiesAsAttributes: true,
bool formatCode: false}) { bool formatCode: false}) {
if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) { if (reflectionEntryPoints == null || reflectionEntryPoints.isEmpty) {
reflectionEntryPoints = entryPoints; reflectionEntryPoints = entryPoints;
@ -94,6 +99,7 @@ class TransformerOptions {
annotationMatcher, annotationMatcher,
optimizationPhases, optimizationPhases,
generateChangeDetectors: generateChangeDetectors, generateChangeDetectors: generateChangeDetectors,
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
inlineViews: inlineViews, inlineViews: inlineViews,
formatCode: formatCode); formatCode: formatCode);
} }

View File

@ -15,6 +15,8 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
var inlineViews = _readBool(config, INLINE_VIEWS_PARAM, defaultValue: true); var inlineViews = _readBool(config, INLINE_VIEWS_PARAM, defaultValue: true);
var generateChangeDetectors = var generateChangeDetectors =
_readBool(config, GENERATE_CHANGE_DETECTORS_PARAM, defaultValue: true); _readBool(config, GENERATE_CHANGE_DETECTORS_PARAM, defaultValue: true);
var reflectPropertiesAsAttributes =
_readBool(config, REFLECT_PROPERTIES_AS_ATTRIBUTES, defaultValue: false);
var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false); var formatCode = _readBool(config, FORMAT_CODE_PARAM, defaultValue: false);
String mirrorModeVal = String mirrorModeVal =
config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : ''; config.containsKey(MIRROR_MODE_PARAM) ? config[MIRROR_MODE_PARAM] : '';
@ -41,6 +43,7 @@ TransformerOptions parseBarbackSettings(BarbackSettings settings) {
customAnnotationDescriptors: _readCustomAnnotations(config), customAnnotationDescriptors: _readCustomAnnotations(config),
optimizationPhases: optimizationPhases, optimizationPhases: optimizationPhases,
generateChangeDetectors: generateChangeDetectors, generateChangeDetectors: generateChangeDetectors,
reflectPropertiesAsAttributes: reflectPropertiesAsAttributes,
formatCode: formatCode); formatCode: formatCode);
} }

View File

@ -399,6 +399,7 @@ class _CodegenState {
var newValue = _names.getLocalName(r.selfIndex); var newValue = _names.getLocalName(r.selfIndex);
var oldValue = _names.getFieldName(r.selfIndex); var oldValue = _names.getFieldName(r.selfIndex);
var notifyDebug = _genConfig.logBindingUpdate ? "this.logBindingUpdate(${newValue});" : "";
var br = r.bindingRecord; var br = r.bindingRecord;
if (br.target.isDirective()) { if (br.target.isDirective()) {
@ -407,12 +408,14 @@ class _CodegenState {
return ''' return '''
${_genThrowOnChangeCheck(oldValue, newValue)} ${_genThrowOnChangeCheck(oldValue, newValue)}
$directiveProperty = $newValue; $directiveProperty = $newValue;
${notifyDebug}
$_IS_CHANGED_LOCAL = true; $_IS_CHANGED_LOCAL = true;
'''; ''';
} else { } else {
return ''' return '''
${_genThrowOnChangeCheck(oldValue, newValue)} ${_genThrowOnChangeCheck(oldValue, newValue)}
this.notifyDispatcher(${newValue}); this.notifyDispatcher(${newValue});
${notifyDebug}
'''; ''';
} }
} }

View File

@ -37,7 +37,7 @@ import 'view_definition_creator.dart';
/// This method assumes a {@link DomAdapter} has been registered. /// This method assumes a {@link DomAdapter} has been registered.
Future<String> processTemplates(AssetReader reader, AssetId entryPoint, Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
{bool generateRegistrations: true, {bool generateRegistrations: true,
bool generateChangeDetectors: true}) async { bool generateChangeDetectors: true, bool reflectPropertiesAsAttributes: false}) async {
var viewDefResults = await createViewDefinitions(reader, entryPoint); var viewDefResults = await createViewDefinitions(reader, entryPoint);
// Note: TemplateCloner(-1) never serializes Nodes into strings. // Note: TemplateCloner(-1) never serializes Nodes into strings.
// we might want to change this to TemplateCloner(0) to force the serialization // we might want to change this to TemplateCloner(0) to force the serialization
@ -58,7 +58,8 @@ Future<String> processTemplates(AssetReader reader, AssetId entryPoint,
} }
if (generateChangeDetectors) { if (generateChangeDetectors) {
var saved = reflector.reflectionCapabilities; var saved = reflector.reflectionCapabilities;
var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled()); var genConfig = new ChangeDetectorGenConfig(assertionsEnabled(), assertionsEnabled(), reflectPropertiesAsAttributes);
reflector.reflectionCapabilities = const NullReflectionCapabilities(); reflector.reflectionCapabilities = const NullReflectionCapabilities();
var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata, var defs = getChangeDetectorDefinitions(viewDefEntry.hostMetadata,
protoView, viewDefEntry.viewDef.directives, genConfig); protoView, viewDefEntry.viewDef.directives, genConfig);

View File

@ -33,7 +33,8 @@ class TemplateCompiler extends Transformer {
var id = transform.primaryInput.id; var id = transform.primaryInput.id;
var reader = new AssetReader.fromTransform(transform); var reader = new AssetReader.fromTransform(transform);
var transformedCode = formatter.format(await processTemplates(reader, id, var transformedCode = formatter.format(await processTemplates(reader, id,
generateChangeDetectors: options.generateChangeDetectors)); generateChangeDetectors: options.generateChangeDetectors,
reflectPropertiesAsAttributes: options.reflectPropertiesAsAttributes));
transform.addOutput(new Asset.fromString(id, transformedCode)); transform.addOutput(new Asset.fromString(id, transformedCode));
}); });
} }

View File

@ -1,7 +1,6 @@
// TODO (jteplitz602): This whole file is nearly identical to core/application.ts. // TODO (jteplitz602): This whole file is nearly identical to core/application.ts.
// There should be a way to refactor application so that this file is unnecessary. See #3277 // There should be a way to refactor application so that this file is unnecessary. See #3277
import {Injector, bind, Binding} from "angular2/di"; import {Injector, bind, Binding} from "angular2/di";
import {Type, isBlank, isPresent} from "angular2/src/facade/lang";
import {Reflector, reflector} from 'angular2/src/reflection/reflection'; import {Reflector, reflector} from 'angular2/src/reflection/reflection';
import {List, ListWrapper} from 'angular2/src/facade/collection'; import {List, ListWrapper} from 'angular2/src/facade/collection';
import { import {
@ -24,7 +23,6 @@ import {AppRootUrl} from 'angular2/src/services/app_root_url';
import { import {
DomRenderer, DomRenderer,
DOCUMENT, DOCUMENT,
DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES,
DefaultDomCompiler, DefaultDomCompiler,
APP_ID_RANDOM_BINDING, APP_ID_RANDOM_BINDING,
MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE, MAX_IN_MEMORY_ELEMENTS_PER_TEMPLATE,
@ -75,12 +73,12 @@ var _rootBindings = [bind(Reflector).toValue(reflector)];
// TODO: This code is nearly identitcal to core/application. There should be a way to only write it // TODO: This code is nearly identitcal to core/application. There should be a way to only write it
// once // once
function _injectorBindings(): List<Type | Binding | List<any>> { function _injectorBindings(): List<any> {
var bestChangeDetection: Type = DynamicChangeDetection; var bestChangeDetection = new DynamicChangeDetection();
if (PreGeneratedChangeDetection.isSupported()) { if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = PreGeneratedChangeDetection; bestChangeDetection = new PreGeneratedChangeDetection();
} else if (JitChangeDetection.isSupported()) { } else if (JitChangeDetection.isSupported()) {
bestChangeDetection = JitChangeDetection; bestChangeDetection = new JitChangeDetection();
} }
return [ return [
@ -94,7 +92,6 @@ function _injectorBindings(): List<Type | Binding | List<any>> {
return new EventManager(plugins, ngZone); return new EventManager(plugins, ngZone);
}, },
[NgZone]), [NgZone]),
bind(DOM_REFLECT_PROPERTIES_AS_ATTRIBUTES).toValue(false),
DomRenderer, DomRenderer,
bind(Renderer).toAlias(DomRenderer), bind(Renderer).toAlias(DomRenderer),
APP_ID_RANDOM_BINDING, APP_ID_RANDOM_BINDING,
@ -119,7 +116,7 @@ function _injectorBindings(): List<Type | Binding | List<any>> {
CompilerCache, CompilerCache,
ViewResolver, ViewResolver,
DEFAULT_PIPES, DEFAULT_PIPES,
bind(ChangeDetection).toClass(bestChangeDetection), bind(ChangeDetection).toValue(bestChangeDetection),
ViewLoader, ViewLoader,
DirectiveResolver, DirectiveResolver,
Parser, Parser,

View File

@ -79,11 +79,11 @@ class PrintLogger {
function _injectorBindings(appComponentType, bus: MessageBusInterface, function _injectorBindings(appComponentType, bus: MessageBusInterface,
initData: StringMap<string, any>): List<Type | Binding | List<any>> { initData: StringMap<string, any>): List<Type | Binding | List<any>> {
var bestChangeDetection: Type = DynamicChangeDetection; var bestChangeDetection = new DynamicChangeDetection();
if (PreGeneratedChangeDetection.isSupported()) { if (PreGeneratedChangeDetection.isSupported()) {
bestChangeDetection = PreGeneratedChangeDetection; bestChangeDetection = new PreGeneratedChangeDetection();
} else if (JitChangeDetection.isSupported()) { } else if (JitChangeDetection.isSupported()) {
bestChangeDetection = JitChangeDetection; bestChangeDetection = new JitChangeDetection();
} }
return [ return [
bind(APP_COMPONENT) bind(APP_COMPONENT)
@ -123,7 +123,7 @@ function _injectorBindings(appComponentType, bus: MessageBusInterface,
DEFAULT_PIPES, DEFAULT_PIPES,
bind(IterableDiffers).toValue(defaultIterableDiffers), bind(IterableDiffers).toValue(defaultIterableDiffers),
bind(KeyValueDiffers).toValue(defaultKeyValueDiffers), bind(KeyValueDiffers).toValue(defaultKeyValueDiffers),
bind(ChangeDetection).toClass(bestChangeDetection), bind(ChangeDetection).toValue(bestChangeDetection),
DirectiveResolver, DirectiveResolver,
PipeResolver, PipeResolver,
Parser, Parser,

View File

@ -28,13 +28,13 @@ export function main() {
it("should return a proto change detector when one is available", () => { it("should return a proto change detector when one is available", () => {
var map = {'id': (def) => proto}; var map = {'id': (def) => proto};
var cd = new PreGeneratedChangeDetection(map); var cd = new PreGeneratedChangeDetection(null, map);
expect(cd.getProtoChangeDetector('id', def)).toBe(proto) expect(cd.getProtoChangeDetector('id', def)).toBe(proto)
}); });
it("should delegate to dynamic change detection otherwise", () => { it("should delegate to dynamic change detection otherwise", () => {
var cd = new PreGeneratedChangeDetection({}); var cd = new PreGeneratedChangeDetection(null, {});
expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector); expect(cd.getProtoChangeDetector('id', def)).toBeAnInstanceOf(DynamicProtoChangeDetector);
}); });
}); });

View File

@ -66,7 +66,7 @@ export var PROP_NAME = 'propName';
* In this case, we expect `id` and `expression` to be the same string. * In this case, we expect `id` and `expression` to be the same string.
*/ */
export function getDefinition(id: string): TestDefinition { export function getDefinition(id: string): TestDefinition {
var genConfig = new ChangeDetectorGenConfig(true, true); var genConfig = new ChangeDetectorGenConfig(true, true, true);
var testDef = null; var testDef = null;
if (StringMapWrapper.contains(_ExpressionWithLocals.availableDefinitions, id)) { if (StringMapWrapper.contains(_ExpressionWithLocals.availableDefinitions, id)) {
let val = StringMapWrapper.get(_ExpressionWithLocals.availableDefinitions, id); let val = StringMapWrapper.get(_ExpressionWithLocals.availableDefinitions, id);
@ -110,6 +110,12 @@ export function getDefinition(id: string): TestDefinition {
var records = _createBindingRecords("a"); var records = _createBindingRecords("a");
let cdDef = new ChangeDetectorDefinition(id, "ON_PUSH_OBSERVE", [], records, [], [], genConfig); let cdDef = new ChangeDetectorDefinition(id, "ON_PUSH_OBSERVE", [], records, [], [], genConfig);
testDef = new TestDefinition(id, cdDef, null); 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)); ListWrapper.concat(allDefs, StringMapWrapper.keys(_DirectiveUpdating.availableDefinitions));
allDefs = ListWrapper.concat(allDefs, _availableEventDefinitions); allDefs = ListWrapper.concat(allDefs, _availableEventDefinitions);
allDefs = ListWrapper.concat(allDefs, _availableHostEventDefinitions); allDefs = ListWrapper.concat(allDefs, _availableHostEventDefinitions);
allDefs = ListWrapper.concat(allDefs, ["onPushObserve"]); allDefs = ListWrapper.concat(allDefs, ["onPushObserve", "updateElementProduction"]);
return ListWrapper.map(allDefs, (id) => getDefinition(id)); return ListWrapper.map(allDefs, (id) => getDefinition(id));
} }
@ -150,7 +156,7 @@ class _ExpressionWithLocals {
var variableBindings = _convertLocalsToVariableBindings(this.locals); var variableBindings = _convertLocalsToVariableBindings(this.locals);
var bindingRecords = _createBindingRecords(this._expression); var bindingRecords = _createBindingRecords(this._expression);
var directiveRecords = []; var directiveRecords = [];
var genConfig = new ChangeDetectorGenConfig(true, true); var genConfig = new ChangeDetectorGenConfig(true, true, true);
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords, return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, bindingRecords,
[], directiveRecords, genConfig); [], directiveRecords, genConfig);
} }
@ -210,7 +216,7 @@ class _ExpressionWithMode {
_createHostEventRecords("(host-event)='false'", dirRecordWithOnPush)) _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, return new ChangeDetectorDefinition('(empty id)', this._strategy, variableBindings,
bindingRecords, eventRecords, directiveRecords, genConfig); bindingRecords, eventRecords, directiveRecords, genConfig);
@ -236,7 +242,7 @@ class _DirectiveUpdating {
createChangeDetectorDefinition(): ChangeDetectorDefinition { createChangeDetectorDefinition(): ChangeDetectorDefinition {
var strategy = null; var strategy = null;
var variableBindings = []; var variableBindings = [];
var genConfig = new ChangeDetectorGenConfig(true, true); var genConfig = new ChangeDetectorGenConfig(true, true, true);
return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings, return new ChangeDetectorDefinition('(empty id)', strategy, variableBindings,
this._bindingRecords, [], this._directiveRecords, this._bindingRecords, [], this._directiveRecords,

View File

@ -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', () => { describe('reading directives', () => {
it('should read directive properties', () => { it('should read directive properties', () => {
var directive = new TestDirective(); var directive = new TestDirective();
@ -1123,7 +1148,8 @@ class FakeDirectives {
} }
class TestDispatcher implements ChangeDispatcher { class TestDispatcher implements ChangeDispatcher {
log: List<string>; log: string[];
debugLog: string[];
loggedValues: List<any>; loggedValues: List<any>;
onAllChangesDoneCalled: boolean = false; onAllChangesDoneCalled: boolean = false;
@ -1131,6 +1157,7 @@ class TestDispatcher implements ChangeDispatcher {
clear() { clear() {
this.log = []; this.log = [];
this.debugLog = [];
this.loggedValues = []; this.loggedValues = [];
this.onAllChangesDoneCalled = true; this.onAllChangesDoneCalled = true;
} }
@ -1140,6 +1167,8 @@ class TestDispatcher implements ChangeDispatcher {
this.loggedValues.push(value); this.loggedValues.push(value);
} }
logBindingUpdate(target, value) { this.debugLog.push(`${target.name}=${this._asString(value)}`); }
notifyOnAllChangesDone() { this.onAllChangesDoneCalled = true; } notifyOnAllChangesDone() { this.onAllChangesDoneCalled = true; }
getDebugContext(a, b) { return null; } getDebugContext(a, b) { return null; }

View File

@ -58,7 +58,10 @@ import {
import { import {
PipeTransform, PipeTransform,
ChangeDetectorRef, ChangeDetectorRef,
ON_PUSH ON_PUSH,
ChangeDetection,
DynamicChangeDetection,
ChangeDetectorGenConfig
} from 'angular2/src/change_detection/change_detection'; } from 'angular2/src/change_detection/change_detection';
import {Directive, Component, View, ViewMetadata, Attribute, Query, Pipe} from 'angular2/metadata'; 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', () => { describe('different proto view storages', () => {
function runWithMode(mode: string) { function runWithMode(mode: string) {

View File

@ -24,8 +24,6 @@ import {
RenderViewRef, RenderViewRef,
ViewEncapsulation ViewEncapsulation
} from 'angular2/src/render/api'; } 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() { export function main() {
describe('DomRenderer integration', () => { 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()) { if (DOM.supportsDOMEvents()) {
it('should call actions on the element independent of the compilation', it('should call actions on the element independent of the compilation',
inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => { inject([AsyncTestCompleter, DomTestbed], (async, tb: DomTestbed) => {

View File

@ -58,6 +58,7 @@ class _MyComponent_ChangeDetector0
} }
this.notifyDispatcher(l_interpolate1); this.notifyDispatcher(l_interpolate1);
this.logBindingUpdate(l_interpolate1);
this.interpolate1 = l_interpolate1; this.interpolate1 = l_interpolate1;
} }

View File

@ -12,6 +12,7 @@ import {
DynamicChangeDetection, DynamicChangeDetection,
JitChangeDetection, JitChangeDetection,
ChangeDetectorDefinition, ChangeDetectorDefinition,
ChangeDetectorGenConfig,
BindingRecord, BindingRecord,
DirectiveRecord, DirectiveRecord,
DirectiveIndex, DirectiveIndex,
@ -249,8 +250,9 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
var dispatcher = new DummyDispatcher(); var dispatcher = new DummyDispatcher();
var parser = new Parser(new Lexer()); var parser = new Parser(new Lexer());
var genConfig = new ChangeDetectorGenConfig(false, false, false);
var parentProto = changeDetection.getProtoChangeDetector( var parentProto = changeDetection.getProtoChangeDetector(
"id", new ChangeDetectorDefinition('parent', null, [], [], [], [], false)); "id", new ChangeDetectorDefinition('parent', null, [], [], [], [], genConfig));
var parentCd = parentProto.instantiate(dispatcher); var parentCd = parentProto.instantiate(dispatcher);
var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)}); var directiveRecord = new DirectiveRecord({directiveIndex: new DirectiveIndex(0, 0)});
@ -279,7 +281,7 @@ function setUpChangeDetection(changeDetection: ChangeDetection, iterations, obje
var proto = changeDetection.getProtoChangeDetector( var proto = changeDetection.getProtoChangeDetector(
"id", "id",
new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], false)); new ChangeDetectorDefinition("proto", null, [], bindings, [], [directiveRecord], genConfig));
var targetObj = new Obj(); var targetObj = new Obj();
parentCd.hydrate(object, null, new FakeDirectives(targetObj), null); parentCd.hydrate(object, null, new FakeDirectives(targetObj), null);
@ -385,6 +387,7 @@ class DummyDispatcher implements ChangeDispatcher {
getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext { getDebugContext(elementIndex: number, directiveIndex: DirectiveIndex): DebugContext {
throw "getDebugContext not implemented."; throw "getDebugContext not implemented.";
} }
notifyOnBinding(bindingRecord, newValue) { throw "Should not be used"; } notifyOnBinding(bindingTarget, newValue) { throw "Should not be used"; }
logBindingUpdate(bindingTarget, newValue) { throw "Should not be used"; }
notifyOnAllChangesDone() {} notifyOnAllChangesDone() {}
} }