feat(change_detection): added support for observable components and directives

This commit is contained in:
vsavkin 2015-08-21 14:45:38 -07:00
parent 85ec34d1d9
commit e8e430e630
11 changed files with 202 additions and 105 deletions

View File

@ -15,6 +15,8 @@ import {Locals} from './parser/locals';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants'; import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile'; import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
import {isObservable} from './observable_facade'; import {isObservable} from './observable_facade';
import {ON_PUSH_OBSERVE} from './constants';
var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`); var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);
@ -44,7 +46,7 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
constructor(public id: string, public dispatcher: ChangeDispatcher, constructor(public id: string, public dispatcher: ChangeDispatcher,
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[], public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
public directiveIndices: DirectiveIndex[], public modeOnHydrate: string) { public directiveIndices: DirectiveIndex[], public strategy: string) {
this.ref = new ChangeDetectorRef(this); this.ref = new ChangeDetectorRef(this);
} }
@ -116,8 +118,13 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// This method is not intended to be overridden. Subclasses should instead provide an // This method is not intended to be overridden. Subclasses should instead provide an
// implementation of `hydrateDirectives`. // implementation of `hydrateDirectives`.
hydrate(context: T, locals: Locals, directives: any, pipes: any): void { hydrate(context: T, locals: Locals, directives: any, pipes: any): void {
this.mode = this.modeOnHydrate; this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
this.context = context; this.context = context;
if (StringWrapper.equals(this.strategy, ON_PUSH_OBSERVE)) {
this.observeComponent(context);
}
this.locals = locals; this.locals = locals;
this.pipes = pipes; this.pipes = pipes;
this.hydrateDirectives(directives); this.hydrateDirectives(directives);
@ -133,7 +140,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
this.dehydrateDirectives(true); this.dehydrateDirectives(true);
// This is an experimental feature. Works only in Dart. // This is an experimental feature. Works only in Dart.
this.unsubsribeFromObservables(); if (StringWrapper.equals(this.strategy, ON_PUSH_OBSERVE)) {
this._unsubsribeFromObservables();
}
this.context = null; this.context = null;
this.locals = null; this.locals = null;
@ -172,7 +181,8 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
} }
} }
private unsubsribeFromObservables(): void { // This is an experimental feature. Works only in Dart.
private _unsubsribeFromObservables(): void {
if (isPresent(this.subscriptions)) { if (isPresent(this.subscriptions)) {
for (var i = 0; i < this.subscriptions.length; ++i) { for (var i = 0; i < this.subscriptions.length; ++i) {
var s = this.subscriptions[i]; var s = this.subscriptions[i];
@ -185,12 +195,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
} }
// This is an experimental feature. Works only in Dart. // This is an experimental feature. Works only in Dart.
protected observe(value: any, index: number): any { protected observeValue(value: any, index: number): any {
if (isObservable(value)) { if (isObservable(value)) {
if (isBlank(this.subscriptions)) { this._createArrayToStoreObservables();
this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
}
if (isBlank(this.subscriptions[index])) { if (isBlank(this.subscriptions[index])) {
this.streams[index] = value.changes; this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.requestCheck()); this.subscriptions[index] = value.changes.listen((_) => this.ref.requestCheck());
@ -203,6 +210,41 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
return value; return value;
} }
// This is an experimental feature. Works only in Dart.
protected observeDirective(value: any, index: number): any {
if (isObservable(value)) {
this._createArrayToStoreObservables();
var arrayIndex = this.numberOfPropertyProtoRecords + index + 2; // +1 is component
this.streams[arrayIndex] = value.changes;
this.subscriptions[arrayIndex] = value.changes.listen((_) => this.ref.requestCheck());
}
return value;
}
// This is an experimental feature. Works only in Dart.
protected observeComponent(value: any): any {
if (isObservable(value)) {
this._createArrayToStoreObservables();
var index = this.numberOfPropertyProtoRecords + 1;
this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.requestCheck());
}
return value;
}
private _createArrayToStoreObservables(): void {
if (isBlank(this.subscriptions)) {
this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords +
this.directiveIndices.length + 2);
this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords +
this.directiveIndices.length + 2);
}
}
protected getDirectiveFor(directives: any, index: number): any {
return directives.getDirectiveFor(this.directiveIndices[index]);
}
protected getDetectorFor(directives: any, index: number): ChangeDetector { protected getDetectorFor(directives: any, index: number): ChangeDetector {
return directives.getDetectorFor(this.directiveIndices[index]); return directives.getDetectorFor(this.directiveIndices[index]);
} }

View File

@ -8,6 +8,7 @@ import {DirectiveIndex, DirectiveRecord} from './directive_record';
import {ProtoRecord, RecordType} from './proto_record'; import {ProtoRecord, RecordType} from './proto_record';
import {CodegenNameUtil, sanitizeName} from './codegen_name_util'; import {CodegenNameUtil, sanitizeName} from './codegen_name_util';
import {CodegenLogicUtil} from './codegen_logic_util'; import {CodegenLogicUtil} from './codegen_logic_util';
import {codify} from './codegen_facade';
import {EventBinding} from './event_binding'; import {EventBinding} from './event_binding';
import {BindingTarget} from './binding_record'; import {BindingTarget} from './binding_record';
import {ChangeDetectorGenConfig} from './interfaces'; import {ChangeDetectorGenConfig} from './interfaces';
@ -48,7 +49,7 @@ export class ChangeDetectorJITGenerator {
${ABSTRACT_CHANGE_DETECTOR}.call( ${ABSTRACT_CHANGE_DETECTOR}.call(
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length}, this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices, ${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices,
"${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}"); ${codify(this.changeDetectionStrategy)});
this.dehydrateDirectives(false); this.dehydrateDirectives(false);
} }
@ -160,7 +161,7 @@ export class ChangeDetectorJITGenerator {
} }
_maybeGenHydrateDirectives(): string { _maybeGenHydrateDirectives(): string {
var hydrateDirectivesCode = this._genHydrateDirectives(); var hydrateDirectivesCode = this._logic.genHydrateDirectives(this.directiveRecords);
var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords); var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords);
if (!hydrateDirectivesCode && !hydrateDetectorsCode) return ''; if (!hydrateDirectivesCode && !hydrateDetectorsCode) return '';
return `${this._typeName}.prototype.hydrateDirectives = function(directives) { return `${this._typeName}.prototype.hydrateDirectives = function(directives) {
@ -169,16 +170,6 @@ export class ChangeDetectorJITGenerator {
}`; }`;
} }
_genHydrateDirectives(): string {
var directiveFieldNames = this._names.getAllDirectiveNames();
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}]);`;
}
return lines.join('\n');
}
_maybeGenCallOnAllChangesDone(): string { _maybeGenCallOnAllChangesDone(): string {
var notifications = []; var notifications = [];
var dirs = this.directiveRecords; var dirs = this.directiveRecords;

View File

@ -5,11 +5,7 @@ import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
import {ProtoRecord, RecordType} from './proto_record'; import {ProtoRecord, RecordType} from './proto_record';
import {BindingTarget} from './binding_record'; import {BindingTarget} from './binding_record';
import {DirectiveRecord} from './directive_record'; import {DirectiveRecord} from './directive_record';
import {ON_PUSH_OBSERVE} from './constants';
/**
* This is an experimental feature. Works only in Dart.
*/
const ON_PUSH_OBSERVE = "ON_PUSH_OBSERVE";
/** /**
* Class responsible for providing change detection logic for chagne detector classes. * Class responsible for providing change detection logic for chagne detector classes.
@ -118,7 +114,7 @@ export class CodegenLogicUtil {
_observe(exp: string, rec: ProtoRecord): string { _observe(exp: string, rec: ProtoRecord): string {
// This is an experimental feature. Works only in Dart. // This is an experimental feature. Works only in Dart.
if (StringWrapper.equals(this._changeDetection, ON_PUSH_OBSERVE)) { if (StringWrapper.equals(this._changeDetection, ON_PUSH_OBSERVE)) {
return `this.observe(${exp}, ${rec.selfIndex})`; return `this.observeValue(${exp}, ${rec.selfIndex})`;
} else { } else {
return exp; return exp;
} }
@ -152,6 +148,24 @@ export class CodegenLogicUtil {
return combineGeneratedStrings(iVals); return combineGeneratedStrings(iVals);
} }
genHydrateDirectives(directiveRecords: DirectiveRecord[]): string {
var res = [];
for (var i = 0; i < directiveRecords.length; ++i) {
var r = directiveRecords[i];
res.push(`${this._names.getDirectiveName(r.directiveIndex)} = ${this._genReadDirective(i)};`);
}
return res.join("\n");
}
private _genReadDirective(index: number) {
// This is an experimental feature. Works only in Dart.
if (StringWrapper.equals(this._changeDetection, ON_PUSH_OBSERVE)) {
return `this.observeDirective(this.getDirectiveFor(directives, ${index}), ${index})`;
} else {
return `this.getDirectiveFor(directives, ${index})`;
}
}
genHydrateDetectors(directiveRecords: DirectiveRecord[]): string { genHydrateDetectors(directiveRecords: DirectiveRecord[]): string {
var res = []; var res = [];
for (var i = 0; i < directiveRecords.length; ++i) { for (var i = 0; i < directiveRecords.length; ++i) {

View File

@ -190,10 +190,6 @@ export class CodegenNameUtil {
return this._addFieldPrefix(`${this._sanitizedNames[idx]}_pipe`); return this._addFieldPrefix(`${this._sanitizedNames[idx]}_pipe`);
} }
getAllDirectiveNames(): List<string> {
return ListWrapper.map(this.directiveRecords, d => this.getDirectiveName(d.directiveIndex));
}
getDirectiveName(d: DirectiveIndex): string { getDirectiveName(d: DirectiveIndex): string {
return this._addFieldPrefix(`directive_${d.name}`); return this._addFieldPrefix(`directive_${d.name}`);
} }

View File

@ -37,4 +37,10 @@ export const DEFAULT: string = "DEFAULT";
export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: string): boolean { export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: string): boolean {
return isBlank(changeDetectionStrategy) || StringWrapper.equals(changeDetectionStrategy, DEFAULT); return isBlank(changeDetectionStrategy) || StringWrapper.equals(changeDetectionStrategy, DEFAULT);
} }
/**
* This is an experimental feature. Works only in Dart.
*/
export const ON_PUSH_OBSERVE = "ON_PUSH_OBSERVE";

View File

@ -14,7 +14,7 @@ import {DirectiveRecord, DirectiveIndex} from './directive_record';
import {Locals} from './parser/locals'; import {Locals} from './parser/locals';
import {ChangeDetectorGenConfig} from './interfaces'; import {ChangeDetectorGenConfig} from './interfaces';
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util'; import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
import {ON_PUSH_OBSERVE} from './constants';
import {ProtoRecord, RecordType} from './proto_record'; import {ProtoRecord, RecordType} from './proto_record';
export class DynamicChangeDetector extends AbstractChangeDetector<any> { export class DynamicChangeDetector extends AbstractChangeDetector<any> {
@ -26,11 +26,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
constructor(id: string, dispatcher: any, numberOfPropertyProtoRecords: number, constructor(id: string, dispatcher: any, numberOfPropertyProtoRecords: number,
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[], propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
modeOnHydrate: string, private records: ProtoRecord[], strategy: string, private records: ProtoRecord[],
private eventBindings: EventBinding[], private directiveRecords: DirectiveRecord[], private eventBindings: EventBinding[], private directiveRecords: DirectiveRecord[],
private genConfig: ChangeDetectorGenConfig) { private genConfig: ChangeDetectorGenConfig) {
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices, super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
modeOnHydrate); strategy);
var len = records.length + 1; var len = records.length + 1;
this.values = ListWrapper.createFixedSize(len); this.values = ListWrapper.createFixedSize(len);
this.localPipes = ListWrapper.createFixedSize(len); this.localPipes = ListWrapper.createFixedSize(len);
@ -87,6 +87,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
hydrateDirectives(directives: any): void { hydrateDirectives(directives: any): void {
this.values[0] = this.context; this.values[0] = this.context;
this.directives = directives; this.directives = directives;
if (StringWrapper.equals(this.strategy, ON_PUSH_OBSERVE)) {
for (var i = 0; i < this.directiveIndices.length; ++i) {
var index = this.directiveIndices[i];
super.observeDirective(directives.getDirectiveFor(index), i);
}
}
} }
dehydrateDirectives(destroyPipes: boolean) { dehydrateDirectives(destroyPipes: boolean) {
@ -211,7 +218,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
return null; return null;
} }
var currValue = this.observe(this._calculateCurrValue(proto, values, locals), proto.selfIndex); var currValue = this._calculateCurrValue(proto, values, locals);
if (StringWrapper.equals(this.strategy, ON_PUSH_OBSERVE)) {
super.observeValue(currValue, proto.selfIndex);
}
if (proto.shouldBeChecked()) { if (proto.shouldBeChecked()) {
var prevValue = this._readSelf(proto, values); var prevValue = this._readSelf(proto, values);
if (!isSame(prevValue, currValue)) { if (!isSame(prevValue, currValue)) {

View File

@ -52,8 +52,7 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
instantiate(dispatcher: any): ChangeDetector { instantiate(dispatcher: any): ChangeDetector {
return new DynamicChangeDetector( return new DynamicChangeDetector(
this.definition.id, dispatcher, this._propertyBindingRecords.length, this.definition.id, dispatcher, this._propertyBindingRecords.length,
this._propertyBindingTargets, this._directiveIndices, this._propertyBindingTargets, this._directiveIndices, this.definition.strategy,
ChangeDetectionUtil.changeDetectionMode(this.definition.strategy),
this._propertyBindingRecords, this._eventBindingRecords, this.definition.directiveRecords, this._propertyBindingRecords, this._eventBindingRecords, this.definition.directiveRecords,
this.definition.genConfig); this.definition.genConfig);
} }

View File

@ -10,6 +10,7 @@ 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/proto_record.dart';
import 'package:angular2/src/change_detection/event_binding.dart'; import 'package:angular2/src/change_detection/event_binding.dart';
import 'package:angular2/src/change_detection/binding_record.dart'; import 'package:angular2/src/change_detection/binding_record.dart';
import 'package:angular2/src/change_detection/codegen_facade.dart' show codify;
import 'package:angular2/src/facade/lang.dart' show BaseException; import 'package:angular2/src/facade/lang.dart' show BaseException;
/// Responsible for generating change detector classes for Angular 2. /// Responsible for generating change detector classes for Angular 2.
@ -74,7 +75,7 @@ class _CodegenState {
/// The name of the generated change detector class. This is an implementation /// The name of the generated change detector class. This is an implementation
/// detail and should not be visible to users. /// detail and should not be visible to users.
final String _changeDetectorTypeName; final String _changeDetectorTypeName;
final String _changeDetectionMode; final String _changeDetectionStrategy;
final List<DirectiveRecord> _directiveRecords; final List<DirectiveRecord> _directiveRecords;
final List<ProtoRecord> _records; final List<ProtoRecord> _records;
final List<EventBinding> _eventBindings; final List<EventBinding> _eventBindings;
@ -87,16 +88,14 @@ class _CodegenState {
this._changeDetectorDefId, this._changeDetectorDefId,
this._contextTypeName, this._contextTypeName,
this._changeDetectorTypeName, this._changeDetectorTypeName,
String changeDetectionStrategy, this._changeDetectionStrategy,
this._records, this._records,
this._propertyBindingTargets, this._propertyBindingTargets,
this._eventBindings, this._eventBindings,
this._directiveRecords, this._directiveRecords,
this._logic, this._logic,
this._names, this._names,
this._genConfig) this._genConfig);
: _changeDetectionMode =
ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy);
factory _CodegenState(String typeName, String changeDetectorTypeName, factory _CodegenState(String typeName, String changeDetectorTypeName,
ChangeDetectorDefinition def) { ChangeDetectorDefinition def) {
@ -130,7 +129,7 @@ class _CodegenState {
dispatcher, ${_records.length}, dispatcher, ${_records.length},
${_changeDetectorTypeName}.gen_propertyBindingTargets, ${_changeDetectorTypeName}.gen_propertyBindingTargets,
${_changeDetectorTypeName}.gen_directiveIndices, ${_changeDetectorTypeName}.gen_directiveIndices,
'$_changeDetectionMode') { ${codify(_changeDetectionStrategy)}) {
dehydrateDirectives(false); dehydrateDirectives(false);
} }
@ -248,7 +247,7 @@ class _CodegenState {
} }
String _maybeGenHydrateDirectives() { String _maybeGenHydrateDirectives() {
var hydrateDirectivesCode = _genHydrateDirectives(); var hydrateDirectivesCode = _logic.genHydrateDirectives(_directiveRecords);
var hydrateDetectorsCode = _logic.genHydrateDetectors(_directiveRecords); var hydrateDetectorsCode = _logic.genHydrateDetectors(_directiveRecords);
if (hydrateDirectivesCode.isEmpty && hydrateDetectorsCode.isEmpty) { if (hydrateDirectivesCode.isEmpty && hydrateDetectorsCode.isEmpty) {
return ''; return '';
@ -257,16 +256,6 @@ class _CodegenState {
'{ $hydrateDirectivesCode $hydrateDetectorsCode }'; '{ $hydrateDirectivesCode $hydrateDetectorsCode }';
} }
String _genHydrateDirectives() {
var buf = new StringBuffer();
var directiveFieldNames = _names.getAllDirectiveNames();
for (var i = 0; i < directiveFieldNames.length; ++i) {
buf.writeln('${directiveFieldNames[i]} = directives.getDirectiveFor('
'${_names.getDirectivesAccessorName()}[$i]);');
}
return '$buf';
}
/// Generates calls to `onAllChangesDone` for all `Directive`s that request /// Generates calls to `onAllChangesDone` for all `Directive`s that request
/// them. /// them.
String _maybeGenCallOnAllChangesDone() { String _maybeGenCallOnAllChangesDone() {

View File

@ -12,6 +12,7 @@ import {
Parser, Parser,
ChangeDetectorGenConfig ChangeDetectorGenConfig
} from 'angular2/src/change_detection/change_detection'; } from 'angular2/src/change_detection/change_detection';
import {ON_PUSH_OBSERVE} from 'angular2/src/change_detection/constants';
import {reflector} from 'angular2/src/reflection/reflection'; import {reflector} from 'angular2/src/reflection/reflection';
import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities'; import {ReflectionCapabilities} from 'angular2/src/reflection/reflection_capabilities';
@ -106,11 +107,19 @@ export function getDefinition(id: string): TestDefinition {
[_DirectiveUpdating.basicRecords[0]], genConfig); [_DirectiveUpdating.basicRecords[0]], genConfig);
testDef = new TestDefinition(id, cdDef, null); testDef = new TestDefinition(id, cdDef, null);
} else if (id == "onPushObserve") { } else if (id == "onPushObserveBinding") {
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 == "onPushObserveComponent") {
let cdDef = new ChangeDetectorDefinition(id, ON_PUSH_OBSERVE, [], [], [], [], genConfig);
testDef = new TestDefinition(id, cdDef, null);
} else if (id == "onPushObserveDirective") {
let cdDef = new ChangeDetectorDefinition(id, ON_PUSH_OBSERVE, [], [], [],
[_DirectiveUpdating.recordNoCallbacks], genConfig);
testDef = new TestDefinition(id, cdDef, null);
} else if (id == "updateElementProduction") { } else if (id == "updateElementProduction") {
var genConfig = new ChangeDetectorGenConfig(false, false, false); var genConfig = new ChangeDetectorGenConfig(false, false, false);
var records = _createBindingRecords("name"); var records = _createBindingRecords("name");
@ -118,7 +127,6 @@ export function getDefinition(id: string): TestDefinition {
testDef = new TestDefinition(id, cdDef, null); testDef = new TestDefinition(id, cdDef, null);
} }
if (isBlank(testDef)) { if (isBlank(testDef)) {
throw `No ChangeDetectorDefinition for ${id} available. Please modify this file if necessary.`; throw `No ChangeDetectorDefinition for ${id} available. Please modify this file if necessary.`;
} }
@ -144,7 +152,12 @@ 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", "updateElementProduction"]); allDefs = ListWrapper.concat(allDefs, [
"onPushObserveBinding",
"onPushObserveComponent",
"onPushObserveDirective",
"updateElementProduction"
]);
return ListWrapper.map(allDefs, (id) => getDefinition(id)); return ListWrapper.map(allDefs, (id) => getDefinition(id));
} }

View File

@ -781,64 +781,100 @@ export function main() {
}); });
if (IS_DART) { if (IS_DART) {
it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable fires an event', describe('ON_PUSH_OBSERVE', () => {
fakeAsync(() => { it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable fires an event',
var context = new TestDirective(); fakeAsync(() => {
context.a = createObservableModel(); var context = new TestDirective();
context.a = createObservableModel();
var cd = _createWithoutHydrate('onPushObserve').changeDetector; var cd = _createWithoutHydrate('onPushObserveBinding').changeDetector;
cd.hydrate(context, null, directives, null); cd.hydrate(context, null, directives, null);
cd.detectChanges(); cd.detectChanges();
expect(cd.mode).toEqual(CHECKED); expect(cd.mode).toEqual(CHECKED);
context.a.pushUpdate(); context.a.pushUpdate();
tick(); tick();
expect(cd.mode).toEqual(CHECK_ONCE); expect(cd.mode).toEqual(CHECK_ONCE);
})); }));
it('should unsubscribe from an old observable when an object changes', fakeAsync(() => { it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable context fires an event',
var originalModel = createObservableModel(); fakeAsync(() => {
var context = new TestDirective(); var context = createObservableModel();
context.a = originalModel;
var cd = _createWithoutHydrate('onPushObserve').changeDetector; var cd = _createWithoutHydrate('onPushObserveComponent').changeDetector;
cd.hydrate(context, null, directives, null); cd.hydrate(context, null, directives, null);
cd.detectChanges(); cd.detectChanges();
context.a = createObservableModel(); expect(cd.mode).toEqual(CHECKED);
cd.mode = CHECK_ONCE;
cd.detectChanges();
// Updating this model will not reenable the detector. This model is not longer context.pushUpdate();
// used. tick();
originalModel.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECKED);
}));
it('should unsubscribe from observables when dehydrating', fakeAsync(() => { expect(cd.mode).toEqual(CHECK_ONCE);
var originalModel = createObservableModel(); }));
var context = new TestDirective();
context.a = originalModel;
var cd = _createWithoutHydrate('onPushObserve').changeDetector; it('should mark ON_PUSH_OBSERVE detectors as CHECK_ONCE when an observable directive fires an event',
cd.hydrate(context, null, directives, null); fakeAsync(() => {
cd.detectChanges(); var dir = createObservableModel();
var directives = new FakeDirectives([dir], []);
cd.dehydrate(); var cd = _createWithoutHydrate('onPushObserveDirective').changeDetector;
cd.hydrate(_DEFAULT_CONTEXT, null, directives, null);
cd.detectChanges();
context.a = "not an observable model"; expect(cd.mode).toEqual(CHECKED);
cd.hydrate(context, null, directives, null);
cd.detectChanges();
// Updating this model will not reenable the detector. This model is not longer dir.pushUpdate();
// used. tick();
originalModel.pushUpdate();
tick(); expect(cd.mode).toEqual(CHECK_ONCE);
expect(cd.mode).toEqual(CHECKED); }));
}));
it('should unsubscribe from an old observable when an object changes',
fakeAsync(() => {
var originalModel = createObservableModel();
var context = new TestDirective();
context.a = originalModel;
var cd = _createWithoutHydrate('onPushObserveBinding').changeDetector;
cd.hydrate(context, null, directives, null);
cd.detectChanges();
context.a = createObservableModel();
cd.mode = CHECK_ONCE;
cd.detectChanges();
// Updating this model will not reenable the detector. This model is not longer
// used.
originalModel.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECKED);
}));
it('should unsubscribe from observables when dehydrating', fakeAsync(() => {
var originalModel = createObservableModel();
var context = new TestDirective();
context.a = originalModel;
var cd = _createWithoutHydrate('onPushObserveBinding').changeDetector;
cd.hydrate(context, null, directives, null);
cd.detectChanges();
cd.dehydrate();
context.a = "not an observable model";
cd.hydrate(context, null, directives, null);
cd.detectChanges();
// Updating this model will not reenable the detector. This model is not longer
// used.
originalModel.pushUpdate();
tick();
expect(cd.mode).toEqual(CHECKED);
}));
});
} }
}); });
}); });

View File

@ -29,7 +29,7 @@ class _MyComponent_ChangeDetector0
_MyComponent_ChangeDetector0(dispatcher) : super( _MyComponent_ChangeDetector0(dispatcher) : super(
"MyComponent_comp_0", dispatcher, 2, "MyComponent_comp_0", dispatcher, 2,
_MyComponent_ChangeDetector0.gen_propertyBindingTargets, _MyComponent_ChangeDetector0.gen_propertyBindingTargets,
_MyComponent_ChangeDetector0.gen_directiveIndices, 'ALWAYS_CHECK') { _MyComponent_ChangeDetector0.gen_directiveIndices,null) {
dehydrateDirectives(false); dehydrateDirectives(false);
} }