Content-Length: 1175959 | pFad | http://github.com/NativeScript/NativeScript/commit/05a6be2e3d2d562ebd570eca94aee13a28be675b

E9 feat(core): implement EventListenerOptions for addEventListener and r… · NativeScript/NativeScript@05a6be2 · GitHub
Skip to content

Commit 05a6be2

Browse files
committed
feat(core): implement EventListenerOptions for addEventListener and removeEventListener
1 parent 5d08e44 commit 05a6be2

File tree

9 files changed

+83
-58
lines changed

9 files changed

+83
-58
lines changed

apps/automated/src/data/observable-tests.ts

+18-15
Original file line numberDiff line numberDiff line change
@@ -286,8 +286,8 @@ export var test_Observable_identity = function () {
286286

287287
// If you try to add the same callback for a given event name twice, without
288288
// distinguishing by its thisArg, the second addition will no-op.
289-
obj.addEventListener(eventName, callback);
290-
obj.addEventListener(eventName, callback);
289+
obj.addEventListener(eventName, callback, false);
290+
obj.addEventListener(eventName, callback, false);
291291
obj.set('testName', 1);
292292
TKUnit.assert(receivedCount === 1, 'Expected Observable to fire exactly once upon a property change, having passed the same callback into addEventListener() twice');
293293
obj.removeEventListener(eventName, callback);
@@ -296,9 +296,9 @@ export var test_Observable_identity = function () {
296296

297297
// All truthy thisArgs are distinct, so we have three distinct identities here
298298
// and they should all get added.
299-
obj.addEventListener(eventName, callback);
300-
obj.addEventListener(eventName, callback, 1);
301-
obj.addEventListener(eventName, callback, 2);
299+
obj.addEventListener(eventName, callback, false);
300+
obj.addEventListener(eventName, callback, false, 1);
301+
obj.addEventListener(eventName, callback, false, 2);
302302
obj.set('testName', 2);
303303
TKUnit.assert(receivedCount === 3, 'Expected Observable to fire exactly three times upon a property change, having passed the same callback into addEventListener() three times, with the latter two distinguished by each having a different truthy thisArg');
304304
obj.removeEventListener(eventName, callback);
@@ -307,24 +307,24 @@ export var test_Observable_identity = function () {
307307

308308
// If you specify thisArg when removing an event listener, it should remove
309309
// just the event listener with the corresponding thisArg.
310-
obj.addEventListener(eventName, callback, 1);
311-
obj.addEventListener(eventName, callback, 2);
310+
obj.addEventListener(eventName, callback, false, 1);
311+
obj.addEventListener(eventName, callback, false, 2);
312312
obj.set('testName', 3);
313313
TKUnit.assert(receivedCount === 2, 'Expected Observable to fire exactly three times upon a property change, having passed the same callback into addEventListener() three times, with the latter two distinguished by each having a different truthy thisArg');
314-
obj.removeEventListener(eventName, callback, 2);
314+
obj.removeEventListener(eventName, callback, false, 2);
315315
TKUnit.assert(obj.hasListeners(eventName), 'Expected removeEventListener(eventName, callback, thisArg) to remove just the event listener that matched the callback and thisArg');
316-
obj.removeEventListener(eventName, callback, 1);
316+
obj.removeEventListener(eventName, callback, false, 1);
317317
TKUnit.assert(!obj.hasListeners(eventName), 'Expected removeEventListener(eventName, callback, thisArg) to remove the remaining event listener that matched the callback and thisArg');
318318
receivedCount = 0;
319319

320320
// All falsy thisArgs are treated alike, so these all have the same identity
321321
// and only the first should get added.
322-
obj.addEventListener(eventName, callback);
323-
obj.addEventListener(eventName, callback, 0);
324322
obj.addEventListener(eventName, callback, false);
325-
obj.addEventListener(eventName, callback, null);
326-
obj.addEventListener(eventName, callback, undefined);
327-
obj.addEventListener(eventName, callback, '');
323+
obj.addEventListener(eventName, callback, false, 0);
324+
obj.addEventListener(eventName, callback, false, false);
325+
obj.addEventListener(eventName, callback, false, null);
326+
obj.addEventListener(eventName, callback, false, undefined);
327+
obj.addEventListener(eventName, callback, false, '');
328328
obj.set('testName', 4);
329329
TKUnit.assert(receivedCount === 1, 'Expected Observable to fire exactly once upon a property change, having passed the same callback into addEventListener() multiple times, each time with a different falsy (and therefore indistinct) thisArg');
330330
obj.removeEventListener(eventName, callback);
@@ -376,7 +376,10 @@ export var test_Observable_removeEventListener_SingleEvent_NoCallbackSpecified =
376376
obj.addEventListener(Observable.propertyChangeEvent, callback2);
377377

378378
obj.set('testName', 1);
379-
obj.removeEventListener(Observable.propertyChangeEvent);
379+
// @ts-expect-error the callback is no longer an optional argument on
380+
// removeEventListener(), but is still optional for off().
381+
TKUnit.assertThrows(() => obj.removeEventListener(Observable.propertyChangeEvent));
382+
obj.off(Observable.propertyChangeEvent);
380383

381384
TKUnit.assert(!obj.hasListeners(Observable.propertyChangeEvent), 'Expected result for hasObservers is false.');
382385

packages/core/accessibility/accessibility-service.android.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ function ensureStateListener(): SharedA11YObservable {
111111
touchExplorationStateChangeListener = null;
112112

113113
if (sharedA11YObservable) {
114-
sharedA11YObservable.removeEventListener(Observable.propertyChangeEvent);
114+
sharedA11YObservable.off(Observable.propertyChangeEvent);
115115
sharedA11YObservable = null;
116116
}
117117

packages/core/accessibility/accessibility-service.ios.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ function getSharedA11YObservable(): SharedA11YObservable {
5656
nativeObserver = null;
5757

5858
if (sharedA11YObservable) {
59-
sharedA11YObservable.removeEventListener(Observable.propertyChangeEvent);
59+
sharedA11YObservable.off(Observable.propertyChangeEvent);
6060

6161
sharedA11YObservable = null;
6262
}

packages/core/data/observable/index.ts

+46-24
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,12 @@ const _globalEventHandlers: {
8989
};
9090
} = {};
9191

92+
/**
93+
* A reusable ready-configured options object for internal use to avoid
94+
* unnecessary allocations.
95+
*/
96+
const onceOption = { once: true } as const satisfies AddEventListenerOptions;
97+
9298
/**
9399
* Observable is used when you want to be notified when a change occurs. Use on/off methods to add/remove listener.
94100
* Please note that should you be using the `new Observable({})` constructor, it is **obsolete** since v3.0,
@@ -160,7 +166,7 @@ export class Observable {
160166
* bound.
161167
*/
162168
public on(eventName: string, callback: (data: EventData) => void, thisArg?: any): void {
163-
this.addEventListener(eventName, callback, thisArg);
169+
this.addEventListener(eventName, callback, onceOption, thisArg);
164170
}
165171

166172
/**
@@ -175,7 +181,7 @@ export class Observable {
175181
* bound.
176182
*/
177183
public once(eventName: string, callback: (data: EventData) => void, thisArg?: any): void {
178-
this.addEventListener(eventName, callback, thisArg, true);
184+
this.addEventListener(eventName, callback, onceOption, thisArg);
179185
}
180186

181187
/**
@@ -188,17 +194,26 @@ export class Observable {
188194
* refine search of the correct event listener to be removed.
189195
*/
190196
public off(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
191-
this.removeEventListener(eventName, callback, thisArg);
197+
this.removeEventListener(eventName, callback, false, thisArg);
192198
}
193199

194200
/**
195201
* Adds a listener for the specified event name.
196-
* @param eventName Name of the event to attach to.
197-
* @param callback A function to be called when some of the specified event(s) is raised.
198-
* @param thisArg An optional parameter which when set will be used as "this" in callback method call.
202+
*
203+
* @param eventName The name of the event.
204+
* @param callback The event listener to add. Will be called when an event of
205+
* the given name is raised.
206+
* @param options An object or boolean indicating the EventListenerOptions.
207+
* - If true, is interpreted as { capture: true }.
208+
* - If false, is interpreted as { capture: false }.
209+
* Note that 'capture' is not implemented at this time anyway, however, so the
210+
* value is ignored. It's purely for API-compatibility with DOM Events.
211+
* @param thisArg An optional parameter which, when set, will be bound as the
212+
* `this` context when the callback is called. Falsy values will be not be
213+
* bound.
199214
*/
200-
public addEventListener(eventName: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
201-
once = once || undefined;
215+
public addEventListener(eventName: string, callback: (data: EventData) => void, options?: AddEventListenerOptions | boolean, thisArg?: any): void {
216+
const once = (typeof options === 'object' && options.once) || undefined;
202217
thisArg = thisArg || undefined;
203218

204219
if (typeof eventName !== 'string') {
@@ -223,20 +238,27 @@ export class Observable {
223238
}
224239

225240
/**
226-
* Removes listener(s) for the specified event name.
227-
* @param eventName Name of the event to attach to.
228-
* @param callback An optional parameter pointing to a specific listener. If not defined, all listeners for the event names will be removed.
229-
* @param thisArg An optional parameter which when set will be used to refine search of the correct callback which will be removed as event listener.
241+
* Removes the listener for the specified event name.
242+
*
243+
* @param eventName The name of the event.
244+
* @param callback The event listener to remove.
245+
* @param _options An object or boolean indicating the EventListenerOptions.
246+
* - If true, is interpreted as { capture: true }.
247+
* - If false, is interpreted as { capture: false }.
248+
* Note that 'capture' is not implemented at this time anyway, however, so the
249+
* value is ignored. It's purely for API-compatibility with DOM Events.
250+
* @param thisArg An optional parameter which, when set, will be used to
251+
* refine search of the correct event listener to be removed.
230252
*/
231-
public removeEventListener(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
253+
public removeEventListener(eventName: string, callback: (data: EventData) => void, _options?: EventListenerOptions | boolean, thisArg?: any): void {
232254
thisArg = thisArg || undefined;
233255

234256
if (typeof eventName !== 'string') {
235257
throw new TypeError('Events name(s) must be string.');
236258
}
237259

238-
if (callback && typeof callback !== 'function') {
239-
throw new TypeError('callback must be function.');
260+
if (typeof callback !== 'function') {
261+
throw new TypeError('Callback must be a function.');
240262
}
241263

242264
const entries = this._observers[eventName];
@@ -257,8 +279,8 @@ export class Observable {
257279
* in future.
258280
* @deprecated
259281
*/
260-
public static on(eventName: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
261-
this.addEventListener(eventName, callback, thisArg, once);
282+
public static on(eventName: string, callback: (data: EventData) => void, thisArg?: any): void {
283+
this.addEventListener(eventName, callback, false, thisArg);
262284
}
263285

264286
/**
@@ -267,7 +289,7 @@ export class Observable {
267289
* @deprecated
268290
*/
269291
public static once(eventName: string, callback: (data: EventData) => void, thisArg?: any): void {
270-
this.addEventListener(eventName, callback, thisArg, true);
292+
this.addEventListener(eventName, callback, onceOption, thisArg);
271293
}
272294

273295
/**
@@ -276,7 +298,7 @@ export class Observable {
276298
* @deprecated
277299
*/
278300
public static off(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
279-
this.removeEventListener(eventName, callback, thisArg);
301+
this.removeEventListener(eventName, callback, false, thisArg);
280302
}
281303

282304
private static innerRemoveEventListener(entries: Array<ListenerEntry>, callback?: (data: EventData) => void, thisArg?: any): void {
@@ -306,15 +328,15 @@ export class Observable {
306328
* in future.
307329
* @deprecated
308330
*/
309-
public static removeEventListener(eventName: string, callback?: (data: EventData) => void, thisArg?: any): void {
331+
public static removeEventListener(eventName: string, callback: (data: EventData) => void, _options?: EventListenerOptions | boolean, thisArg?: any): void {
310332
thisArg = thisArg || undefined;
311333

312334
if (typeof eventName !== 'string') {
313335
throw new TypeError('Event name must be a string.');
314336
}
315337

316-
if (callback && typeof callback !== 'function') {
317-
throw new TypeError('Callback, if provided, must be function.');
338+
if (typeof callback !== 'function') {
339+
throw new TypeError('Callback must be a function.');
318340
}
319341

320342
const eventClass = this.name === 'Observable' ? '*' : this.name;
@@ -343,8 +365,8 @@ export class Observable {
343365
* in future.
344366
* @deprecated
345367
*/
346-
public static addEventListener(eventName: string, callback: (data: EventData) => void, thisArg?: any, once?: boolean): void {
347-
once = once || undefined;
368+
public static addEventListener(eventName: string, callback: (data: EventData) => void, options?: AddEventListenerOptions | boolean, thisArg?: any): void {
369+
const once = (typeof options === 'object' && options.once) || undefined;
348370
thisArg = thisArg || undefined;
349371

350372
if (typeof eventName !== 'string') {

packages/core/ui/core/view/view-common.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -297,11 +297,11 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
297297
return this._gestureObservers[type];
298298
}
299299

300-
public addEventListener(eventNames: string, callback: (data: EventData) => void, thisArg?: any) {
300+
public addEventListener(eventName: string, callback: (data: EventData) => void, options?: AddEventListenerOptions | boolean, thisArg?: any) {
301301
thisArg = thisArg || undefined;
302302

303303
// Normalize "ontap" -> "tap"
304-
const normalizedName = getEventOrGestureName(eventNames);
304+
const normalizedName = getEventOrGestureName(eventName);
305305

306306
// Coerce "tap" -> GestureTypes.tap
307307
// Coerce "loaded" -> undefined
@@ -313,14 +313,14 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
313313
return;
314314
}
315315

316-
super.addEventListener(normalizedName, callback, thisArg);
316+
super.addEventListener(normalizedName, callback, options, thisArg);
317317
}
318318

319-
public removeEventListener(eventNames: string, callback?: (data: EventData) => void, thisArg?: any) {
319+
public removeEventListener(eventName: string, callback?: (data: EventData) => void, options?: EventListenerOptions | boolean, thisArg?: any) {
320320
thisArg = thisArg || undefined;
321321

322322
// Normalize "ontap" -> "tap"
323-
const normalizedName = getEventOrGestureName(eventNames);
323+
const normalizedName = getEventOrGestureName(eventName);
324324

325325
// Coerce "tap" -> GestureTypes.tap
326326
// Coerce "loaded" -> undefined
@@ -332,7 +332,7 @@ export abstract class ViewCommon extends ViewBase implements ViewDefinition {
332332
return;
333333
}
334334

335-
super.removeEventListener(normalizedName, callback, thisArg);
335+
super.removeEventListener(normalizedName, callback, options, thisArg);
336336
}
337337

338338
public onBackPressed(): boolean {

packages/core/ui/core/weak-event-listener/index.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function getHandlerForEventName(eventName: string): (eventData: EventData) => vo
2121
const sourceEventMap = sourcesMap.get(source);
2222
if (!sourceEventMap) {
2323
// There is no event map for this source - it is safe to detach the listener;
24-
source.removeEventListener(eventName, handlersForEventName.get(eventName));
24+
source.off(eventName, handlersForEventName.get(eventName));
2525

2626
return;
2727
}
@@ -46,7 +46,7 @@ function getHandlerForEventName(eventName: string): (eventData: EventData) => vo
4646

4747
if (deadPairsIndexes.length === targetHandlerPairList.length) {
4848
// There are no alive targets for this event - unsubscribe
49-
source.removeEventListener(eventName, handlersForEventName.get(eventName));
49+
source.off(eventName, handlersForEventName.get(eventName));
5050
sourceEventMap.delete(eventName);
5151
} else {
5252
for (let j = deadPairsIndexes.length - 1; j >= 0; j--) {

packages/core/ui/scroll-view/scroll-view-common.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
1717

1818
private _addedScrollEvent = false;
1919

20-
public addEventListener(arg: string, callback: (data: EventData) => void, thisArg?: any): void {
20+
public addEventListener(arg: string, callback: (data: EventData) => void, options?: AddEventListenerOptions | boolean, thisArg?: any): void {
2121
const hasExistingScrollListeners: boolean = this.hasListeners(ScrollViewBase.scrollEvent);
2222

23-
super.addEventListener(arg, callback, thisArg);
23+
super.addEventListener(arg, callback, options, thisArg);
2424

2525
// This indicates that a scroll listener was added for first time
2626
if (!hasExistingScrollListeners && this.hasListeners(ScrollViewBase.scrollEvent)) {
@@ -32,10 +32,10 @@ export abstract class ScrollViewBase extends ContentView implements ScrollViewDe
3232
}
3333
}
3434

35-
public removeEventListener(arg: string, callback?: (data: EventData) => void, thisArg?: any): void {
35+
public removeEventListener(arg: string, callback?: (data: EventData) => void, options?: EventListenerOptions | boolean, thisArg?: any): void {
3636
const hasExistingScrollListeners: boolean = this.hasListeners(ScrollViewBase.scrollEvent);
3737

38-
super.removeEventListener(arg, callback, thisArg);
38+
super.removeEventListener(arg, callback, options, thisArg);
3939

4040
// This indicates that the final scroll listener was removed
4141
if (hasExistingScrollListeners && !this.hasListeners(ScrollViewBase.scrollEvent)) {

packages/core/ui/text-base/formatted-string.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export class FormattedString extends ViewBase implements FormattedStringDefiniti
1414
constructor() {
1515
super();
1616
this._spans = new ObservableArray<Span>();
17-
this._spans.addEventListener(ObservableArray.changeEvent, this.onSpansCollectionChanged, this);
17+
this._spans.addEventListener(ObservableArray.changeEvent, this.onSpansCollectionChanged, false, this);
1818
}
1919

2020
get fontFamily(): string {

packages/core/ui/text-base/span.ts

+4-4
Original file line numberDiff line numberDiff line change
@@ -109,13 +109,13 @@ export class Span extends ViewBase implements SpanDefinition {
109109
return this._tappable;
110110
}
111111

112-
addEventListener(arg: string, callback: (data: EventData) => void, thisArg?: any): void {
113-
super.addEventListener(arg, callback, thisArg);
112+
addEventListener(arg: string, callback: (data: EventData) => void, options?: AddEventListenerOptions | boolean, thisArg?: any): void {
113+
super.addEventListener(arg, callback, options, thisArg);
114114
this._setTappable(this.hasListeners(Span.linkTapEvent));
115115
}
116116

117-
removeEventListener(arg: string, callback?: (data: EventData) => void, thisArg?: any): void {
118-
super.removeEventListener(arg, callback, thisArg);
117+
removeEventListener(arg: string, callback?: (data: EventData) => void, options?: EventListenerOptions | boolean, thisArg?: any): void {
118+
super.removeEventListener(arg, callback, options, thisArg);
119119
this._setTappable(this.hasListeners(Span.linkTapEvent));
120120
}
121121

0 commit comments

Comments
 (0)








ApplySandwichStrip

pFad - (p)hone/(F)rame/(a)nonymizer/(d)eclutterfier!      Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

Fetched URL: http://github.com/NativeScript/NativeScript/commit/05a6be2e3d2d562ebd570eca94aee13a28be675b

Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy