WeakEvents with map

This commit is contained in:
vakrilov
2015-05-15 16:55:02 +03:00
parent 54f6c350de
commit 57171c768e
8 changed files with 251 additions and 302 deletions

View File

@ -225,6 +225,7 @@ export function forceGC() {
if (platform.device.os === platform.platformNames.ios) { if (platform.device.os === platform.platformNames.ios) {
// Could cause GC on the next call. // Could cause GC on the next call.
new ArrayBuffer(4 * 1024 * 1024); new ArrayBuffer(4 * 1024 * 1024);
TKUnit.wait(ASYNC);
} }
utils.GC(); utils.GC();
} }

View File

@ -4,89 +4,64 @@ import observable = require("data/observable");
import weakEvents = require("ui/core/weak-event-listener"); import weakEvents = require("ui/core/weak-event-listener");
import helper = require("./ui/helper"); import helper = require("./ui/helper");
class Target {
public counter: number = 0;
public onEvent(data: observable.EventData) {
this.counter++;
}
}
export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_source() { export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_source() {
TKUnit.assertThrows(() => { TKUnit.assertThrows(() => {
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(undefined, "eventName", emptyHandler, {});
source: undefined,
target: {},
handler: emptyHandler,
eventName: observable.Observable.propertyChangeEvent
});
}); });
} }
export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_target() { export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_target() {
TKUnit.assertThrows(() => { TKUnit.assertThrows(() => {
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(new observable.Observable(), "eventName", emptyHandler, undefined);
source: new observable.Observable(),
target: undefined,
handler: emptyHandler,
eventName: observable.Observable.propertyChangeEvent
});
}); });
} }
export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_handler() { export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_handler() {
TKUnit.assertThrows(() => { TKUnit.assertThrows(() => {
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(new observable.Observable(), "eventName", undefined, {});
source: new observable.Observable(),
target: {},
handler: undefined,
eventName: observable.Observable.propertyChangeEvent
});
}); });
} }
export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_name() { export function test_addWeakEventListener_throwsWhenCalledwitnInvalid_name() {
TKUnit.assertThrows(() => { TKUnit.assertThrows(() => {
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(new observable.Observable(), undefined, emptyHandler, {});
source: new observable.Observable(),
target: {},
handler: emptyHandler,
eventName: undefined
});
}); });
} }
export function test_addWeakEventListener_listensForEvent() { export function test_addWeakEventListener_listensForEvent() {
var source = new observable.Observable(); var source = new observable.Observable();
var target = new Object; var target = new Target();
var callbackCalled = false;
var handler = function (args: observable.EventData) {
callbackCalled = true;
}
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(
source: source, source,
target: target, observable.Observable.propertyChangeEvent,
handler: handler, target.onEvent,
eventName: observable.Observable.propertyChangeEvent target);
})
helper.forceGC();
source.set("testProp", "some value"); source.set("testProp", "some value");
TKUnit.assert(callbackCalled, "Handler not called."); TKUnit.assertEqual(target.counter, 1, "Handler not called.");
} }
export function test_removeWeakEventListener_StopsListeningForEvet() { export function test_removeWeakEventListener_StopsListeningForEvet() {
var source = new observable.Observable(); var source = new observable.Observable();
var target = new Object; var target = new Target();
var callbackCalled = false;
var handler = function (args: observable.EventData) {
callbackCalled = true;
}
var listenerID = weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(source, observable.Observable.propertyChangeEvent, target.onEvent, target);
source: source, weakEvents.removeWeakEventListener(source, observable.Observable.propertyChangeEvent, target.onEvent, target)
target: target,
handler: handler,
eventName: observable.Observable.propertyChangeEvent
})
weakEvents.WeakEventListener.removeWeakEventListener(listenerID);
source.set("testProp", "some value"); source.set("testProp", "some value");
TKUnit.assert(!callbackCalled, "Handler should not be called."); TKUnit.assertEqual(target.counter, 0, "Handler should not be called.");
} }
export function test_handlerIsCalled_WithTargetAsThis() { export function test_handlerIsCalled_WithTargetAsThis() {
@ -98,12 +73,7 @@ export function test_handlerIsCalled_WithTargetAsThis() {
callbackCalled = true; callbackCalled = true;
} }
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(source, observable.Observable.propertyChangeEvent, handler, target);
source: source,
target: target,
handler: handler,
eventName: observable.Observable.propertyChangeEvent
})
source.set("testProp", "some value"); source.set("testProp", "some value");
TKUnit.assert(callbackCalled, "Handler not called."); TKUnit.assert(callbackCalled, "Handler not called.");
@ -111,19 +81,10 @@ export function test_handlerIsCalled_WithTargetAsThis() {
export function test_listnerDoesNotRetainTarget() { export function test_listnerDoesNotRetainTarget() {
var source = new observable.Observable(); var source = new observable.Observable();
var target = new Object; var target = new Target();
var callbackCalled = false; var callbackCalled = false;
var handler = function (args: observable.EventData) {
TKUnit.assertEqual(this, target, "this should be the target");
callbackCalled = true;
}
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(source, observable.Observable.propertyChangeEvent, target.onEvent, target);
source: source,
target: target,
handler: handler,
eventName: observable.Observable.propertyChangeEvent
})
var targetRef = new WeakRef(target); var targetRef = new WeakRef(target);
target = undefined; target = undefined;
@ -134,19 +95,10 @@ export function test_listnerDoesNotRetainTarget() {
export function test_listnerDoesNotRetainSource() { export function test_listnerDoesNotRetainSource() {
var source = new observable.Observable(); var source = new observable.Observable();
var target = new Object(); var target = new Target();
var callbackCalled = false; var callbackCalled = false;
var handler = function (args: observable.EventData) {
TKUnit.assertEqual(this, target, "this should be the target");
callbackCalled = true;
}
weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(source, observable.Observable.propertyChangeEvent, target.onEvent, target);
source: source,
target: target,
handler: handler,
eventName: observable.Observable.propertyChangeEvent
})
var sourceRef = new WeakRef(source); var sourceRef = new WeakRef(source);
source = undefined; source = undefined;
@ -155,50 +107,74 @@ export function test_listnerDoesNotRetainSource() {
TKUnit.assert(!sourceRef.get(), "Source should be released after GC"); TKUnit.assert(!sourceRef.get(), "Source should be released after GC");
} }
export function test_listnerIsCleared_WhenTargetIsDead() { //export function test_listnerIsCleared_WhenTargetIsDead() {
var source = new observable.Observable(); // var source = new observable.Observable();
var listenerID = addListenerWithSource(source); // var listenerID = addListenerWithSource(source);
helper.forceGC(); // helper.forceGC();
for (var i = 0; i < weakEvents.WeakEventListener.cleanDeadReferencesCountTrigger; i++) { // for (var i = 0; i < weakEvents.cleanDeadReferencesCountTrigger; i++) {
addListenerWithSource(source); // addListenerWithSource(source);
} // }
TKUnit.assert(types.isUndefined(weakEvents.WeakEventListener._weakEventListeners[listenerID]), "The first listener should be dead by now"); // TKUnit.assert(types.isUndefined(weakEvents._weakEventListeners[listenerID]), "The first listener should be dead by now");
} //}
export function test_listnerIsCleared_WhenSourceIsDead() { //export function test_listnerIsCleared_WhenSourceIsDead() {
var target = {}; // var target = {};
var listenerID = addListenerWithTarget(target); // var listenerID = addListenerWithTarget(target);
helper.forceGC(); // helper.forceGC();
for (var i = 0; i < weakEvents.WeakEventListener.cleanDeadReferencesCountTrigger; i++) { // for (var i = 0; i < weakEvents.cleanDeadReferencesCountTrigger; i++) {
addListenerWithTarget(target); // addListenerWithTarget(target);
} // }
TKUnit.assert(types.isUndefined(weakEvents.WeakEventListener._weakEventListeners[listenerID]), "The first listener should be dead by now"); // TKUnit.assert(types.isUndefined(weakEvents._weakEventListeners[listenerID]), "The first listener should be dead by now");
} //}
function addListenerWithSource(source: observable.Observable): number { //function addListenerWithSource(source: observable.Observable): number {
return weakEvents.WeakEventListener.addWeakEventListener({ // return weakEvents.addWeakEventListener({
source: source, // source: source,
target: {}, // target: {},
handler: emptyHandler, // handler: emptyHandler,
eventName: observable.Observable.propertyChangeEvent // eventName: observable.Observable.propertyChangeEvent
}) // })
} //}
function addListenerWithTarget(target: any): number { //function addListenerWithTarget(target: any): number {
return weakEvents.WeakEventListener.addWeakEventListener({ // return weakEvents.addWeakEventListener({
source: new observable.Observable(), // source: new observable.Observable(),
target: target, // target: target,
handler: emptyHandler, // handler: emptyHandler,
eventName: observable.Observable.propertyChangeEvent // eventName: observable.Observable.propertyChangeEvent
}) // })
} //}
function emptyHandler(data: observable.EventData) { function emptyHandler(data: observable.EventData) {
// Do nothing. // Do nothing.
} }
export function testWeakMap(): void {
var source = new observable.Observable();
var target = new Target();
var targetRef = new WeakRef(target);
var weakMap = new WeakMap<observable.Observable, Target>();
weakMap.set(source, target);
TKUnit.assertEqual(weakMap.get(source), target, "target");
target = undefined;
source = undefined;
helper.forceGC();
TKUnit.wait(1);
TKUnit.waitUntilReady(function () {
return false;
})
TKUnit.assert(!targetRef.get(), "Target should be dead");
}

View File

@ -4,7 +4,7 @@ import definition = require("ui/button");
import proxy = require("ui/core/proxy"); import proxy = require("ui/core/proxy");
import formattedString = require("text/formatted-string"); import formattedString = require("text/formatted-string");
import observable = require("data/observable"); import observable = require("data/observable");
import weakEventListener = require("ui/core/weak-event-listener"); import weakEvents = require("ui/core/weak-event-listener");
var textProperty = new dependencyObservable.Property( var textProperty = new dependencyObservable.Property(
"text", "text",
@ -37,8 +37,6 @@ export class Button extends view.View implements definition.Button {
public static textProperty = textProperty; public static textProperty = textProperty;
public static formattedTextProperty = formattedTextProperty; public static formattedTextProperty = formattedTextProperty;
private _formattedTextWeakListenerId: number;
public _onBindingContextChanged(oldValue: any, newValue: any) { public _onBindingContextChanged(oldValue: any, newValue: any) {
super._onBindingContextChanged(oldValue, newValue); super._onBindingContextChanged(oldValue, newValue);
if (this.formattedText) { if (this.formattedText) {
@ -61,16 +59,11 @@ export class Button extends view.View implements definition.Button {
set formattedText(value: formattedString.FormattedString) { set formattedText(value: formattedString.FormattedString) {
if (this.formattedText !== value) { if (this.formattedText !== value) {
if (this.formattedText) { if (this.formattedText) {
weakEventListener.WeakEventListener.removeWeakEventListener(this._formattedTextWeakListenerId); weakEvents.removeWeakEventListener(this.formattedText, observable.Observable.propertyChangeEvent, this.onFormattedTextChanged, this);
} }
this._setValue(Button.formattedTextProperty, value); this._setValue(Button.formattedTextProperty, value);
if (value) { if (value) {
this._formattedTextWeakListenerId = weakEventListener.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(value, observable.Observable.propertyChangeEvent, this.onFormattedTextChanged, this);
target: this,
source: value,
eventName: observable.Observable.propertyChangeEvent,
handler: this.onFormattedTextChanged
});
} }
} }
} }

View File

@ -162,12 +162,11 @@ export class Binding {
if (this.sourceOptions) { if (this.sourceOptions) {
var sourceOptionsInstance = this.sourceOptions.instance.get(); var sourceOptionsInstance = this.sourceOptions.instance.get();
if (sourceOptionsInstance instanceof observable.Observable) { if (sourceOptionsInstance instanceof observable.Observable) {
this.weakEventId = weakEvents.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(
target: this, sourceOptionsInstance,
source: this.sourceOptions.instance.get(), observable.Observable.propertyChangeEvent,
eventName: observable.Observable.propertyChangeEvent, this.onSourcePropertyChanged,
handler: this.onSourcePropertyChanged, this);
});
} }
} }
} }
@ -177,8 +176,15 @@ export class Binding {
return; return;
} }
weakEvents.WeakEventListener.removeWeakEventListener(this.weakEventId); if (this.sourceOptions) {
delete this.weakEventId; var sourceOptionsInstance = this.sourceOptions.instance.get();
if (sourceOptionsInstance) {
weakEvents.removeWeakEventListener(sourceOptionsInstance,
observable.Observable.propertyChangeEvent,
this.onSourcePropertyChanged,
this);
}
}
if (this.source) { if (this.source) {
this.source.clear(); this.source.clear();

View File

@ -2,56 +2,20 @@ declare module "ui/core/weak-event-listener" {
import observable = require("data/observable"); import observable = require("data/observable");
/** /**
* An interface that defines all options needed for creating weak event listener. * Creates and initialize WeakEventListener.
* @param source Observable class which emits the event.
* @param eventName The event name.
* @param handler The function which should be called when event occurs.
* @param target Subscriber (target) of the event listener. It will be used as a thisArg in the handler function.
*/ */
export interface WeakEventListenerOptions { export function addWeakEventListener(source: observable.Observable, eventName: string, handler: (eventData: observable.EventData) => void, target: any) : void;
/**
* Subscriber (target) of the event listener. It will be used as a thisArg in the handler function.
*/
target: any;
/** /**
* Observable class which emits the event. * Removes and clears all resources from WeakEventListener.
* @param source Observable class which emits the event.
* @param eventName The event name.
* @param handler The function which should be called when event occurs.
* @param target Subscriber (target) of the event listener. It will be used as a thisArg in the handler function.
*/ */
source: observable.Observable; export function removeWeakEventListener(source: observable.Observable, eventName: string, handler: (eventData: observable.EventData) => void, target: any): void;
/**
* Name of the event.
*/
eventName: string;
/**
* The function which should be called when event occurs.
*/
handler: (eventData: observable.EventData) => void;
}
/**
* Represents a class that utilize work with weak event listeners.
*/
export class WeakEventListener {
/**
* Creates and initialize WeakEventListener (if all required options are set).
* @param options An instance of WeakEventListenerOptions needed to create WeakEventListener instance.
* Returns The id of the WeakEventListener object to be used in removeWeakEventListener method.
*/
static addWeakEventListener(options: WeakEventListenerOptions): number;
/**
* Removes and clears all resources from WeakEventListener with given id.
* @param The id of the WeakEventListener object.
*/
static removeWeakEventListener(listenerId: number): void;
/**
* Specifies how often will internal clearing of dead references will occur.
* Clearing will be triggered on addWeakEventListener on every N times where N is
* the value of this property. Set 0 to disable clearing.
*/
static cleanDeadReferencesCountTrigger: number;
//@private
static _weakEventListeners: Object;
//@endprivate
}
} }

View File

@ -2,120 +2,142 @@
import definition = require("ui/core/weak-event-listener"); import definition = require("ui/core/weak-event-listener");
import types = require("utils/types"); import types = require("utils/types");
var CLEAN_TRIGGER_DEFAULT = 1000; var handlersForEventName = new Map<string,(eventData: observable.EventData) => void>();
export class WeakEventListener implements definition.WeakEventListener { var sourcesMap = new WeakMap<observable.Observable, Map<string, Array<TargetHandlerPair>>>();
private id: number;
private targetRef: WeakRef<any>;
private senderRef: WeakRef<observable.Observable>;
private eventName: string;
private handler: (eventData: observable.EventData) => void;
static _idCounter: number = 0; class TargetHandlerPair {
static _weakEventListeners = {}; tagetRef: WeakRef<Object>;
static _cleanDeadReferencesCountTrigger = CLEAN_TRIGGER_DEFAULT; handler: (eventData: observable.EventData) => void;
static get cleanDeadReferencesCountTrigger(): number {
return WeakEventListener._cleanDeadReferencesCountTrigger; constructor(target: Object, handler: (eventData: observable.EventData) => void) {
this.tagetRef = new WeakRef(target);
this.handler = handler;
} }
static set cleanDeadReferencesCountTrigger(value: number) { }
WeakEventListener._cleanDeadReferencesCountTrigger = value;
function getHandlerForEventName(eventName: string): (eventData: observable.EventData) => void {
var handler = handlersForEventName.get(eventName);
if (!handler) {
var handler = function (eventData: observable.EventData) {
var source = eventData.object;
var sourceEventMap = sourcesMap.get(source);
if (!sourceEventMap) {
// There is no event map for this source - it is safe to detach the listener;
source.removeEventListener(eventName, handlersForEventName.get(eventName));
return;
} }
private handlerCallback(eventData) { var targetHandlerPairList = sourceEventMap.get(eventName);
var target = this.targetRef.get(); if (!targetHandlerPairList) {
return;
}
var deadPairsIndexes = [];
for (var i = 0; i < targetHandlerPairList.length; i++) {
var pair = targetHandlerPairList[i];
var target = pair.tagetRef.get();
if (target) { if (target) {
this.handler.call(target, eventData); pair.handler.call(target, eventData);
} }
else { else {
// The target is dead - we can unsubscribe; deadPairsIndexes.push(i);
WeakEventListener.removeWeakEventListener(this.id);
} }
} }
private init(options: definition.WeakEventListenerOptions, listenerId: number) { if (deadPairsIndexes.length === targetHandlerPairList.length) {
this.id = listenerId; // There are no alive targets for this event - unsubscribe
this.targetRef = new WeakRef(options.target); source.removeEventListener(eventName, handlersForEventName.get(eventName));
this.senderRef = new WeakRef(options.source); sourceEventMap.delete(eventName);
this.eventName = options.eventName; }
this.handler = options.handler; else {
for (var j = deadPairsIndexes.length - 1; j >= 0; j--) {
var sourceInstance = this.senderRef.get(); targetHandlerPairList.splice(deadPairsIndexes[j], 1);
if (sourceInstance) {
sourceInstance.addEventListener(this.eventName, this.handlerCallback, this);
} }
} }
};
private clear() { handlersForEventName.set(eventName, handler);
var sender = this.senderRef.get();
if (sender) {
sender.removeEventListener(this.eventName, this.handlerCallback, this);
} }
this.targetRef.clear(); return handler;
this.targetRef = undefined; }
this.senderRef.clear();
this.senderRef = undefined;
this.eventName = undefined; function validateArgs(source: observable.Observable, eventName: string, handler: (eventData: observable.EventData) => void, target: any) {
this.handler = undefined; if (types.isNullOrUndefined(source)) {
throw new Error("source is null or undefined");
} }
static addWeakEventListener(options: definition.WeakEventListenerOptions) { if (types.isNullOrUndefined(target)) {
if (types.isNullOrUndefined(options.target)) { throw new Error("target is null or undefined");
throw new Error("targetWeakRef is null or undefined");
} }
if (types.isNullOrUndefined(options.source)) { if (!types.isString(eventName)) {
throw new Error("sourceWeakRef is null or undefined");
}
if (!types.isString(options.eventName)) {
throw new Error("eventName is not a string"); throw new Error("eventName is not a string");
} }
if (!types.isFunction(options.handler)) { if (!types.isFunction(handler)) {
throw new Error("handler is not a function"); throw new Error("handler is not a function");
} }
}
var listenerId = WeakEventListener._idCounter++; export function addWeakEventListener(source: observable.Observable, eventName: string, handler: (eventData: observable.EventData) => void, target: any) {
var weakEventListener = new WeakEventListener(); validateArgs(source, eventName, handler, target);
weakEventListener.init(options, listenerId);
WeakEventListener._weakEventListeners[listenerId] = new WeakRef(weakEventListener); var sourceEventMap = sourcesMap.get(source);
if (!sourceEventMap) {
if (WeakEventListener._cleanDeadReferencesCountTrigger && sourceEventMap = new Map<string, Array<TargetHandlerPair>>();
(listenerId % WeakEventListener._cleanDeadReferencesCountTrigger) === 0) { sourcesMap.set(source, sourceEventMap);
WeakEventListener._cleanDeadReferences();
} }
return listenerId; var pairList = sourceEventMap.get(eventName);
if (!pairList) {
pairList = new Array<TargetHandlerPair>();
sourceEventMap.set(eventName, pairList);
} }
static removeWeakEventListener(listenerId: number) { pairList.push(new TargetHandlerPair(target, handler));
var listenerRef = <WeakRef<WeakEventListener>>WeakEventListener._weakEventListeners[listenerId];
if (listenerRef) { source.addEventListener(eventName, getHandlerForEventName(eventName));
var listener = <WeakEventListener>listenerRef.get(); }
if (listener) {
listener.clear(); export function removeWeakEventListener(source: observable.Observable, eventName: string, handler: (eventData: observable.EventData) => void, target: any) {
validateArgs(source, eventName, handler, target);
var handlerForEventWithName = handlersForEventName.get(eventName);
if (!handlerForEventWithName) {
// We have never created handler for event with this name;
return;
}
var sourceEventMap = sourcesMap.get(source);
if (!sourceEventMap) {
return;
}
var targetHandlerPairList = sourceEventMap.get(eventName);
if (!targetHandlerPairList) {
return;
}
// Remove all pairs that match given target and handler or have a dead target
var targetHandlerPairsToRemove = [];
for (var i = 0; i < targetHandlerPairList.length; i++) {
var pair = targetHandlerPairList[i];
var registeredTarget = pair.tagetRef.get();
if (!registeredTarget || (registeredTarget === target && handler === pair.handler)) {
targetHandlerPairsToRemove.push(i);
} }
} }
delete WeakEventListener._weakEventListeners[listenerId]; if (targetHandlerPairsToRemove.length === targetHandlerPairList.length) {
// There are no alive targets for this event - unsubscribe
source.removeEventListener(eventName, handlerForEventWithName);
sourceEventMap.delete(eventName);
} }
else {
static _cleanDeadReferences() { for (var j = targetHandlerPairsToRemove.length - 1; j >= 0; j--) {
var deadListeners = new Array<number>(); targetHandlerPairList.splice(targetHandlerPairsToRemove[j], 1);
for (var id in WeakEventListener._weakEventListeners) {
var listenerRef = <WeakRef<WeakEventListener>>WeakEventListener._weakEventListeners[id];
var listener = listenerRef.get();
if (!listener || !listener.targetRef.get() || !listener.senderRef.get()) {
deadListeners.push(id);
}
}
for (var i = 0; i < deadListeners.length; i++) {
WeakEventListener.removeWeakEventListener(deadListeners[i]);
} }
} }
} }

View File

@ -7,7 +7,7 @@ import dependencyObservable = require("ui/core/dependency-observable");
import builder = require("ui/builder"); import builder = require("ui/builder");
import label = require("ui/label"); import label = require("ui/label");
import color = require("color"); import color = require("color");
import weakEventListener = require("ui/core/weak-event-listener"); import weakEvents = require("ui/core/weak-event-listener");
import types = require("utils/types"); import types = require("utils/types");
var ITEMS = "items"; var ITEMS = "items";
@ -132,17 +132,12 @@ export class ListView extends view.View implements definition.ListView {
public _onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) { public _onItemsPropertyChanged(data: dependencyObservable.PropertyChangeData) {
if (data.oldValue instanceof observable.Observable && types.isDefined(this._itemsChangedWeakListenerId)) { if (data.oldValue instanceof observable.Observable && types.isDefined(this._itemsChangedWeakListenerId)) {
weakEventListener.WeakEventListener.removeWeakEventListener(this._itemsChangedWeakListenerId); weakEvents.removeWeakEventListener(data.oldValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this);
delete this._itemsChangedWeakListenerId; delete this._itemsChangedWeakListenerId;
} }
if (data.newValue instanceof observable.Observable) { if (data.newValue instanceof observable.Observable) {
this._itemsChangedWeakListenerId = weakEventListener.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(data.newValue, observableArray.ObservableArray.changeEvent, this._onItemsChanged, this);
target: this,
source: data.newValue,
eventName: observableArray.ObservableArray.changeEvent,
handler: this._onItemsChanged,
});
} }
this.refresh(); this.refresh();

View File

@ -4,7 +4,7 @@ import observable = require("data/observable");
import dependencyObservable = require("ui/core/dependency-observable"); import dependencyObservable = require("ui/core/dependency-observable");
import proxy = require("ui/core/proxy"); import proxy = require("ui/core/proxy");
import formattedString = require("text/formatted-string"); import formattedString = require("text/formatted-string");
import weakEventListener = require("ui/core/weak-event-listener"); import weakEvents = require("ui/core/weak-event-listener");
import utils = require("utils/utils"); import utils = require("utils/utils");
import trace = require("trace"); import trace = require("trace");
@ -38,8 +38,6 @@ export class TextBase extends view.View implements definition.TextBase {
public static textProperty = textProperty; public static textProperty = textProperty;
public static formattedTextProperty = formattedTextProperty; public static formattedTextProperty = formattedTextProperty;
private _formattedTextChangedListenerId: number;
constructor(options?: definition.Options) { constructor(options?: definition.Options) {
super(options); super(options);
} }
@ -80,17 +78,11 @@ export class TextBase extends view.View implements definition.TextBase {
set formattedText(value: formattedString.FormattedString) { set formattedText(value: formattedString.FormattedString) {
if (this.formattedText !== value) { if (this.formattedText !== value) {
if (this.formattedText) { if (this.formattedText) {
weakEventListener.WeakEventListener.removeWeakEventListener(this._formattedTextChangedListenerId); weakEvents.removeWeakEventListener(this.formattedText, observable.Observable.propertyChangeEvent, this.onFormattedTextChanged, this);
delete this._formattedTextChangedListenerId;
} }
this._setValue(TextBase.formattedTextProperty, value); this._setValue(TextBase.formattedTextProperty, value);
if (value) { if (value) {
this._formattedTextChangedListenerId = weakEventListener.WeakEventListener.addWeakEventListener({ weakEvents.addWeakEventListener(value, observable.Observable.propertyChangeEvent, this.onFormattedTextChanged, this);
target: this,
source: value,
eventName: observable.Observable.propertyChangeEvent,
handler: this.onFormattedTextChanged,
});
} }
} }
} }