feat(change_detection): added support for observable components and directives
This commit is contained in:
@ -15,6 +15,8 @@ import {Locals} from './parser/locals';
|
||||
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants';
|
||||
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
|
||||
import {isObservable} from './observable_facade';
|
||||
import {ON_PUSH_OBSERVE} from './constants';
|
||||
|
||||
|
||||
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,
|
||||
public numberOfPropertyProtoRecords: number, public bindingTargets: BindingTarget[],
|
||||
public directiveIndices: DirectiveIndex[], public modeOnHydrate: string) {
|
||||
public directiveIndices: DirectiveIndex[], public strategy: string) {
|
||||
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
|
||||
// implementation of `hydrateDirectives`.
|
||||
hydrate(context: T, locals: Locals, directives: any, pipes: any): void {
|
||||
this.mode = this.modeOnHydrate;
|
||||
this.mode = ChangeDetectionUtil.changeDetectionMode(this.strategy);
|
||||
this.context = context;
|
||||
|
||||
if (StringWrapper.equals(this.strategy, ON_PUSH_OBSERVE)) {
|
||||
this.observeComponent(context);
|
||||
}
|
||||
|
||||
this.locals = locals;
|
||||
this.pipes = pipes;
|
||||
this.hydrateDirectives(directives);
|
||||
@ -133,7 +140,9 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
this.dehydrateDirectives(true);
|
||||
|
||||
// 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.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)) {
|
||||
for (var i = 0; i < this.subscriptions.length; ++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.
|
||||
protected observe(value: any, index: number): any {
|
||||
protected observeValue(value: any, index: number): any {
|
||||
if (isObservable(value)) {
|
||||
if (isBlank(this.subscriptions)) {
|
||||
this.subscriptions = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
|
||||
this.streams = ListWrapper.createFixedSize(this.numberOfPropertyProtoRecords + 1);
|
||||
}
|
||||
this._createArrayToStoreObservables();
|
||||
if (isBlank(this.subscriptions[index])) {
|
||||
this.streams[index] = value.changes;
|
||||
this.subscriptions[index] = value.changes.listen((_) => this.ref.requestCheck());
|
||||
@ -203,6 +210,41 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
|
||||
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 {
|
||||
return directives.getDetectorFor(this.directiveIndices[index]);
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import {DirectiveIndex, DirectiveRecord} from './directive_record';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {CodegenNameUtil, sanitizeName} from './codegen_name_util';
|
||||
import {CodegenLogicUtil} from './codegen_logic_util';
|
||||
import {codify} from './codegen_facade';
|
||||
import {EventBinding} from './event_binding';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {ChangeDetectorGenConfig} from './interfaces';
|
||||
@ -48,7 +49,7 @@ export class ChangeDetectorJITGenerator {
|
||||
${ABSTRACT_CHANGE_DETECTOR}.call(
|
||||
this, ${JSON.stringify(this.id)}, dispatcher, ${this.records.length},
|
||||
${this._typeName}.gen_propertyBindingTargets, ${this._typeName}.gen_directiveIndices,
|
||||
"${ChangeDetectionUtil.changeDetectionMode(this.changeDetectionStrategy)}");
|
||||
${codify(this.changeDetectionStrategy)});
|
||||
this.dehydrateDirectives(false);
|
||||
}
|
||||
|
||||
@ -160,7 +161,7 @@ export class ChangeDetectorJITGenerator {
|
||||
}
|
||||
|
||||
_maybeGenHydrateDirectives(): string {
|
||||
var hydrateDirectivesCode = this._genHydrateDirectives();
|
||||
var hydrateDirectivesCode = this._logic.genHydrateDirectives(this.directiveRecords);
|
||||
var hydrateDetectorsCode = this._logic.genHydrateDetectors(this.directiveRecords);
|
||||
if (!hydrateDirectivesCode && !hydrateDetectorsCode) return '';
|
||||
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 {
|
||||
var notifications = [];
|
||||
var dirs = this.directiveRecords;
|
||||
|
@ -5,11 +5,7 @@ import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
import {BindingTarget} from './binding_record';
|
||||
import {DirectiveRecord} from './directive_record';
|
||||
|
||||
/**
|
||||
* This is an experimental feature. Works only in Dart.
|
||||
*/
|
||||
const ON_PUSH_OBSERVE = "ON_PUSH_OBSERVE";
|
||||
import {ON_PUSH_OBSERVE} from './constants';
|
||||
|
||||
/**
|
||||
* Class responsible for providing change detection logic for chagne detector classes.
|
||||
@ -118,7 +114,7 @@ export class CodegenLogicUtil {
|
||||
_observe(exp: string, rec: ProtoRecord): string {
|
||||
// This is an experimental feature. Works only in Dart.
|
||||
if (StringWrapper.equals(this._changeDetection, ON_PUSH_OBSERVE)) {
|
||||
return `this.observe(${exp}, ${rec.selfIndex})`;
|
||||
return `this.observeValue(${exp}, ${rec.selfIndex})`;
|
||||
} else {
|
||||
return exp;
|
||||
}
|
||||
@ -152,6 +148,24 @@ export class CodegenLogicUtil {
|
||||
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 {
|
||||
var res = [];
|
||||
for (var i = 0; i < directiveRecords.length; ++i) {
|
||||
|
@ -190,10 +190,6 @@ export class CodegenNameUtil {
|
||||
return this._addFieldPrefix(`${this._sanitizedNames[idx]}_pipe`);
|
||||
}
|
||||
|
||||
getAllDirectiveNames(): List<string> {
|
||||
return ListWrapper.map(this.directiveRecords, d => this.getDirectiveName(d.directiveIndex));
|
||||
}
|
||||
|
||||
getDirectiveName(d: DirectiveIndex): string {
|
||||
return this._addFieldPrefix(`directive_${d.name}`);
|
||||
}
|
||||
|
@ -37,4 +37,10 @@ export const DEFAULT: string = "DEFAULT";
|
||||
|
||||
export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: string): boolean {
|
||||
return isBlank(changeDetectionStrategy) || StringWrapper.equals(changeDetectionStrategy, DEFAULT);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is an experimental feature. Works only in Dart.
|
||||
*/
|
||||
export const ON_PUSH_OBSERVE = "ON_PUSH_OBSERVE";
|
@ -14,7 +14,7 @@ import {DirectiveRecord, DirectiveIndex} from './directive_record';
|
||||
import {Locals} from './parser/locals';
|
||||
import {ChangeDetectorGenConfig} from './interfaces';
|
||||
import {ChangeDetectionUtil, SimpleChange} from './change_detection_util';
|
||||
|
||||
import {ON_PUSH_OBSERVE} from './constants';
|
||||
import {ProtoRecord, RecordType} from './proto_record';
|
||||
|
||||
export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
@ -26,11 +26,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
|
||||
constructor(id: string, dispatcher: any, numberOfPropertyProtoRecords: number,
|
||||
propertyBindingTargets: BindingTarget[], directiveIndices: DirectiveIndex[],
|
||||
modeOnHydrate: string, private records: ProtoRecord[],
|
||||
strategy: string, private records: ProtoRecord[],
|
||||
private eventBindings: EventBinding[], private directiveRecords: DirectiveRecord[],
|
||||
private genConfig: ChangeDetectorGenConfig) {
|
||||
super(id, dispatcher, numberOfPropertyProtoRecords, propertyBindingTargets, directiveIndices,
|
||||
modeOnHydrate);
|
||||
strategy);
|
||||
var len = records.length + 1;
|
||||
this.values = ListWrapper.createFixedSize(len);
|
||||
this.localPipes = ListWrapper.createFixedSize(len);
|
||||
@ -87,6 +87,13 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
hydrateDirectives(directives: any): void {
|
||||
this.values[0] = this.context;
|
||||
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) {
|
||||
@ -211,7 +218,11 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
|
||||
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()) {
|
||||
var prevValue = this._readSelf(proto, values);
|
||||
if (!isSame(prevValue, currValue)) {
|
||||
|
@ -52,8 +52,7 @@ export class DynamicProtoChangeDetector implements ProtoChangeDetector {
|
||||
instantiate(dispatcher: any): ChangeDetector {
|
||||
return new DynamicChangeDetector(
|
||||
this.definition.id, dispatcher, this._propertyBindingRecords.length,
|
||||
this._propertyBindingTargets, this._directiveIndices,
|
||||
ChangeDetectionUtil.changeDetectionMode(this.definition.strategy),
|
||||
this._propertyBindingTargets, this._directiveIndices, this.definition.strategy,
|
||||
this._propertyBindingRecords, this._eventBindingRecords, this.definition.directiveRecords,
|
||||
this.definition.genConfig);
|
||||
}
|
||||
|
@ -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/event_binding.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;
|
||||
|
||||
/// 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
|
||||
/// detail and should not be visible to users.
|
||||
final String _changeDetectorTypeName;
|
||||
final String _changeDetectionMode;
|
||||
final String _changeDetectionStrategy;
|
||||
final List<DirectiveRecord> _directiveRecords;
|
||||
final List<ProtoRecord> _records;
|
||||
final List<EventBinding> _eventBindings;
|
||||
@ -87,16 +88,14 @@ class _CodegenState {
|
||||
this._changeDetectorDefId,
|
||||
this._contextTypeName,
|
||||
this._changeDetectorTypeName,
|
||||
String changeDetectionStrategy,
|
||||
this._changeDetectionStrategy,
|
||||
this._records,
|
||||
this._propertyBindingTargets,
|
||||
this._eventBindings,
|
||||
this._directiveRecords,
|
||||
this._logic,
|
||||
this._names,
|
||||
this._genConfig)
|
||||
: _changeDetectionMode =
|
||||
ChangeDetectionUtil.changeDetectionMode(changeDetectionStrategy);
|
||||
this._genConfig);
|
||||
|
||||
factory _CodegenState(String typeName, String changeDetectorTypeName,
|
||||
ChangeDetectorDefinition def) {
|
||||
@ -130,7 +129,7 @@ class _CodegenState {
|
||||
dispatcher, ${_records.length},
|
||||
${_changeDetectorTypeName}.gen_propertyBindingTargets,
|
||||
${_changeDetectorTypeName}.gen_directiveIndices,
|
||||
'$_changeDetectionMode') {
|
||||
${codify(_changeDetectionStrategy)}) {
|
||||
dehydrateDirectives(false);
|
||||
}
|
||||
|
||||
@ -248,7 +247,7 @@ class _CodegenState {
|
||||
}
|
||||
|
||||
String _maybeGenHydrateDirectives() {
|
||||
var hydrateDirectivesCode = _genHydrateDirectives();
|
||||
var hydrateDirectivesCode = _logic.genHydrateDirectives(_directiveRecords);
|
||||
var hydrateDetectorsCode = _logic.genHydrateDetectors(_directiveRecords);
|
||||
if (hydrateDirectivesCode.isEmpty && hydrateDetectorsCode.isEmpty) {
|
||||
return '';
|
||||
@ -257,16 +256,6 @@ class _CodegenState {
|
||||
'{ $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
|
||||
/// them.
|
||||
String _maybeGenCallOnAllChangesDone() {
|
||||
|
Reference in New Issue
Block a user