X Tutup
Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
53 changes: 47 additions & 6 deletions modules/angular2/src/change_detection/abstract_change_detector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import {isPresent, isBlank, BaseException} from 'angular2/src/facade/lang';
import {isPresent, isBlank, BaseException, StringWrapper} from 'angular2/src/facade/lang';
import {List, ListWrapper} from 'angular2/src/facade/collection';
import {ChangeDetectionUtil} from './change_detection_util';
import {ChangeDetectorRef} from './change_detector_ref';
Expand All @@ -13,8 +13,9 @@ import {
import {ProtoRecord} from './proto_record';
import {BindingRecord} from './binding_record';
import {Locals} from './parser/locals';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED} from './constants';
import {wtfCreateScope, wtfLeave, WtfScopeFn} from '../profile/profile';
import {isObservable} from './observable_facade';

var _scope_check: WtfScopeFn = wtfCreateScope(`ChangeDetector#check(ascii id, bool throwOnChange)`);

Expand Down Expand Up @@ -42,6 +43,10 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
firstProtoInCurrentBinding: number;
protos: List<ProtoRecord>;

// 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) {
this.ref = new ChangeDetectorRef(this);
Expand Down Expand Up @@ -79,13 +84,14 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
checkNoChanges(): void { throw new BaseException("Not implemented"); }

runDetectChanges(throwOnChange: boolean): void {
if (this.mode === DETACHED || this.mode === CHECKED) return;
if (StringWrapper.equals(this.mode, DETACHED) || StringWrapper.equals(this.mode, CHECKED))
return;
var s = _scope_check(this.id, throwOnChange);
this.detectChangesInRecords(throwOnChange);
this._detectChangesInLightDomChildren(throwOnChange);
if (throwOnChange === false) this.callOnAllChangesDone();
this._detectChangesInShadowDomChildren(throwOnChange);
if (this.mode === CHECK_ONCE) this.mode = CHECKED;
if (StringWrapper.equals(this.mode, CHECK_ONCE)) this.mode = CHECKED;
wtfLeave(s);
}

Expand Down Expand Up @@ -132,6 +138,10 @@ export class AbstractChangeDetector<T> implements ChangeDetector {
// implementation of `dehydrateDirectives`.
dehydrate(): void {
this.dehydrateDirectives(true);

// This is an experimental feature. Works only in Dart.
this.unsubsribeFromObservables();

this.context = null;
this.locals = null;
this.pipes = null;
Expand Down Expand Up @@ -163,12 +173,43 @@ export class AbstractChangeDetector<T> implements ChangeDetector {

markPathToRootAsCheckOnce(): void {
var c: ChangeDetector = this;
while (isPresent(c) && c.mode != DETACHED) {
if (c.mode === CHECKED) c.mode = CHECK_ONCE;
while (isPresent(c) && !StringWrapper.equals(c.mode, DETACHED)) {
if (StringWrapper.equals(c.mode, CHECKED)) c.mode = CHECK_ONCE;
c = c.parent;
}
}

private unsubsribeFromObservables(): void {
if (isPresent(this.subscriptions)) {
for (var i = 0; i < this.subscriptions.length; ++i) {
var s = this.subscriptions[i];
if (isPresent(this.subscriptions[i])) {
s.cancel();
this.subscriptions[i] = null;
}
}
}
}

// This is an experimental feature. Works only in Dart.
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);
}
if (isBlank(this.subscriptions[index])) {
this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.requestCheck());
} else if (this.streams[index] !== value.changes) {
this.subscriptions[index].cancel();
this.streams[index] = value.changes;
this.subscriptions[index] = value.changes.listen((_) => this.ref.requestCheck());
}
}
return value;
}

protected notifyDispatcher(value: any): void {
this.dispatcher.notifyOnBinding(this._currentBinding(), value);
}
Expand Down
4 changes: 2 additions & 2 deletions modules/angular2/src/change_detection/binding_record.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ export class BindingRecord {
return isPresent(this.directiveRecord) && this.directiveRecord.callOnChange;
}

isOnPushChangeDetection(): boolean {
return isPresent(this.directiveRecord) && this.directiveRecord.isOnPushChangeDetection();
isDefaultChangeDetection(): boolean {
return isBlank(this.directiveRecord) || this.directiveRecord.isDefaultChangeDetection();
}

isDirective(): boolean { return this.mode === DIRECTIVE; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ export class ChangeDetectorJITGenerator {
public directiveRecords: List<any>, private generateCheckNoChanges: boolean) {
this._names =
new CodegenNameUtil(this.records, this.eventBindings, this.directiveRecords, UTIL);
this._logic = new CodegenLogicUtil(this._names, UTIL);
this._logic = new CodegenLogicUtil(this._names, UTIL, changeDetectionStrategy);
this._typeName = sanitizeName(`ChangeDetector_${this.id}`);
}

Expand Down Expand Up @@ -116,10 +116,10 @@ export class ChangeDetectorJITGenerator {

_genMarkPathToRootAsCheckOnce(r: ProtoRecord): string {
var br = r.bindingRecord;
if (br.isOnPushChangeDetection()) {
return `${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markPathToRootAsCheckOnce();`;
} else {
if (br.isDefaultChangeDetection()) {
return "";
} else {
return `${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markPathToRootAsCheckOnce();`;
}
}

Expand Down Expand Up @@ -369,7 +369,7 @@ export class ChangeDetectorJITGenerator {

_genNotifyOnPushDetectors(r: ProtoRecord): string {
var br = r.bindingRecord;
if (!r.lastInDirective || !br.isOnPushChangeDetection()) return "";
if (!r.lastInDirective || br.isDefaultChangeDetection()) return "";
var retVal = `
if(${IS_CHANGED_LOCAL}) {
${this._names.getDetectorName(br.directiveRecord.directiveIndex)}.markAsCheckOnce();
Expand Down
19 changes: 16 additions & 3 deletions modules/angular2/src/change_detection/change_detection_util.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,20 @@
import {CONST_EXPR, isPresent, isBlank, BaseException, Type} from 'angular2/src/facade/lang';
import {
CONST_EXPR,
isPresent,
isBlank,
BaseException,
Type,
StringWrapper
} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';
import {ProtoRecord} from './proto_record';
import {CHECK_ALWAYS, CHECK_ONCE, CHECKED, DETACHED, ON_PUSH} from './constants';
import {
CHECK_ALWAYS,
CHECK_ONCE,
CHECKED,
DETACHED,
isDefaultChangeDetectionStrategy
} from './constants';
import {implementsOnDestroy} from './pipe_lifecycle_reflector';


Expand Down Expand Up @@ -165,7 +178,7 @@ export class ChangeDetectionUtil {
}

static changeDetectionMode(strategy: string): string {
return strategy == ON_PUSH ? CHECK_ONCE : CHECK_ALWAYS;
return isDefaultChangeDetectionStrategy(strategy) ? CHECK_ALWAYS : CHECK_ONCE;
}

static simpleChange(previousValue: any, currentValue: any): SimpleChange {
Expand Down
33 changes: 25 additions & 8 deletions modules/angular2/src/change_detection/codegen_logic_util.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import {ListWrapper} from 'angular2/src/facade/collection';
import {BaseException, Json} from 'angular2/src/facade/lang';
import {BaseException, Json, StringWrapper} from 'angular2/src/facade/lang';
import {CodegenNameUtil} from './codegen_name_util';
import {codify, combineGeneratedStrings, rawString} from './codegen_facade';
import {ProtoRecord, RecordType} from './proto_record';

/**
* 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.
*/
export class CodegenLogicUtil {
constructor(private _names: CodegenNameUtil, private _utilName: string) {}
constructor(private _names: CodegenNameUtil, private _utilName: string,
private _changeDetection: string) {}

/**
* Generates a statement which updates the local variable representing `protoRec` with the current
Expand Down Expand Up @@ -46,28 +52,31 @@ export class CodegenLogicUtil {
break;

case RecordType.PROPERTY_READ:
rhs = `${context}.${protoRec.name}`;
rhs = this._observe(`${context}.${protoRec.name}`, protoRec);
break;

case RecordType.SAFE_PROPERTY:
rhs = `${this._utilName}.isValueBlank(${context}) ? null : ${context}.${protoRec.name}`;
var read = this._observe(`${context}.${protoRec.name}`, protoRec);
rhs =
`${this._utilName}.isValueBlank(${context}) ? null : ${this._observe(read, protoRec)}`;
break;

case RecordType.PROPERTY_WRITE:
rhs = `${context}.${protoRec.name} = ${getLocalName(protoRec.args[0])}`;
break;

case RecordType.LOCAL:
rhs = `${localsAccessor}.get(${rawString(protoRec.name)})`;
rhs = this._observe(`${localsAccessor}.get(${rawString(protoRec.name)})`, protoRec);
break;

case RecordType.INVOKE_METHOD:
rhs = `${context}.${protoRec.name}(${argString})`;
rhs = this._observe(`${context}.${protoRec.name}(${argString})`, protoRec);
break;

case RecordType.SAFE_INVOKE_METHOD:
var invoke = `${context}.${protoRec.name}(${argString})`;
rhs =
`${this._utilName}.isValueBlank(${context}) ? null : ${context}.${protoRec.name}(${argString})`;
`${this._utilName}.isValueBlank(${context}) ? null : ${this._observe(invoke, protoRec)}`;
break;

case RecordType.INVOKE_CLOSURE:
Expand All @@ -87,7 +96,7 @@ export class CodegenLogicUtil {
break;

case RecordType.KEYED_READ:
rhs = `${context}[${getLocalName(protoRec.args[0])}]`;
rhs = this._observe(`${context}[${getLocalName(protoRec.args[0])}]`, protoRec);
break;

case RecordType.KEYED_WRITE:
Expand All @@ -104,6 +113,14 @@ export class CodegenLogicUtil {
return `${getLocalName(protoRec.selfIndex)} = ${rhs};`;
}

_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})`;
} else {
return exp;
}
}

_genInterpolation(protoRec: ProtoRecord): string {
var iVals = [];
Expand Down
6 changes: 3 additions & 3 deletions modules/angular2/src/change_detection/codegen_name_util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ export class CodegenNameUtil {
MapWrapper.forEach(this._sanitizedEventNames, (names, eb) => {
for (var i = 0; i < names.length; ++i) {
if (i !== CONTEXT_INDEX) {
res.push(this.getEventLocalName(eb, i));
res.push(`${this.getEventLocalName(eb, i)}`);
}
}
});
Expand Down Expand Up @@ -155,7 +155,7 @@ export class CodegenNameUtil {
for (var j = 0, jLen = this.directiveRecords.length; j < jLen; ++j) {
var dRec = this.directiveRecords[j];
fieldList.push(this.getDirectiveName(dRec.directiveIndex));
if (dRec.isOnPushChangeDetection()) {
if (!dRec.isDefaultChangeDetection()) {
fieldList.push(this.getDetectorName(dRec.directiveIndex));
}
}
Expand Down Expand Up @@ -202,7 +202,7 @@ export class CodegenNameUtil {

getAllDetectorNames(): List<string> {
return ListWrapper.map(
ListWrapper.filter(this.directiveRecords, r => r.isOnPushChangeDetection()),
ListWrapper.filter(this.directiveRecords, r => !r.isDefaultChangeDetection()),
(d) => this.getDetectorName(d.directiveIndex));
}

Expand Down
5 changes: 5 additions & 0 deletions modules/angular2/src/change_detection/constants.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// TODO:vsavkin Use enums after switching to TypeScript
import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/facade/lang';

/**
* CHECK_ONCE means that after calling detectChanges the mode of the change detector
Expand Down Expand Up @@ -33,3 +34,7 @@ export const ON_PUSH: string = "ON_PUSH";
* DEFAULT means that the change detector's mode will be set to CHECK_ALWAYS during hydration.
*/
export const DEFAULT: string = "DEFAULT";

export function isDefaultChangeDetectionStrategy(changeDetectionStrategy: string): boolean {
return isBlank(changeDetectionStrategy) || StringWrapper.equals(changeDetectionStrategy, DEFAULT);
}
8 changes: 5 additions & 3 deletions modules/angular2/src/change_detection/directive_record.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {ON_PUSH} from './constants';
import {StringWrapper, normalizeBool} from 'angular2/src/facade/lang';
import {StringWrapper, normalizeBool, isBlank} from 'angular2/src/facade/lang';
import {isDefaultChangeDetectionStrategy} from './constants';

export class DirectiveIndex {
constructor(public elementIndex: number, public directiveIndex: number) {}
Expand Down Expand Up @@ -32,5 +32,7 @@ export class DirectiveRecord {
this.changeDetection = changeDetection;
}

isOnPushChangeDetection(): boolean { return StringWrapper.equals(this.changeDetection, ON_PUSH); }
isDefaultChangeDetection(): boolean {
return isDefaultChangeDetectionStrategy(this.changeDetection);
}
}
14 changes: 10 additions & 4 deletions modules/angular2/src/change_detection/dynamic_change_detector.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import {isPresent, isBlank, BaseException, FunctionWrapper} from 'angular2/src/facade/lang';
import {
isPresent,
isBlank,
BaseException,
FunctionWrapper,
StringWrapper
} from 'angular2/src/facade/lang';
import {List, ListWrapper, MapWrapper, StringMapWrapper} from 'angular2/src/facade/collection';

import {AbstractChangeDetector} from './abstract_change_detector';
Expand Down Expand Up @@ -64,7 +70,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
}

_markPathAsCheckOnce(proto: ProtoRecord): void {
if (proto.bindingRecord.isOnPushChangeDetection()) {
if (!proto.bindingRecord.isDefaultChangeDetection()) {
var dir = proto.bindingRecord.directiveRecord;
this._getDetectorFor(dir.directiveIndex).markPathToRootAsCheckOnce();
}
Expand Down Expand Up @@ -136,7 +142,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {

if (proto.lastInDirective) {
changes = null;
if (isChanged && bindingRecord.isOnPushChangeDetection()) {
if (isChanged && !bindingRecord.isDefaultChangeDetection()) {
this._getDetectorFor(directiveRecord.directiveIndex).markAsCheckOnce();
}

Expand Down Expand Up @@ -198,7 +204,7 @@ export class DynamicChangeDetector extends AbstractChangeDetector<any> {
return null;
}

var currValue = this._calculateCurrValue(proto, values, locals);
var currValue = this.observe(this._calculateCurrValue(proto, values, locals), proto.selfIndex);
if (proto.shouldBeChecked()) {
var prevValue = this._readSelf(proto, values);
if (!isSame(prevValue, currValue)) {
Expand Down
3 changes: 3 additions & 0 deletions modules/angular2/src/change_detection/observable_facade.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import 'package:observe/observe.dart';

bool isObservable(value) => value is Observable;
3 changes: 3 additions & 0 deletions modules/angular2/src/change_detection/observable_facade.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export function isObservable(value: any): boolean {
return false;
}
4 changes: 1 addition & 3 deletions modules/angular2/src/directives/observable_list_diff.dart
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,8 @@ class ObservableListDiff extends DefaultIterableDiffer {
return super.diff(collection);

// No updates has been registered.
// Returning this tells change detection that object has not change,
// so it should NOT update the binding.
} else {
return this;
return null;
}
}
}
Expand Down
Loading
X Tutup