Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[CLEANUP beta] remove addArrayObserver #19746

Closed
wants to merge 1 commit into from
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
8 changes: 1 addition & 7 deletions packages/@ember/-internals/metal/index.ts
Expand Up @@ -4,13 +4,7 @@ export { default as alias } from './lib/alias';
export { deprecateProperty } from './lib/deprecate_property';
export { PROXY_CONTENT, _getPath, get, _getProp } from './lib/property_get';
export { set, _setProp, trySet } from './lib/property_set';
export {
objectAt,
replace,
replaceInNativeArray,
addArrayObserver,
removeArrayObserver,
} from './lib/array';
export { objectAt, replace, replaceInNativeArray } from './lib/array';
export { arrayContentWillChange, arrayContentDidChange } from './lib/array_events';
export { eachProxyArrayWillChange, eachProxyArrayDidChange } from './lib/each_proxy_events';
export { addListener, hasListeners, on, removeListener, sendEvent } from './lib/events';
Expand Down
90 changes: 1 addition & 89 deletions packages/@ember/-internals/metal/lib/array.ts
@@ -1,15 +1,8 @@
import { EmberArray, getDebugName } from '@ember/-internals/utils';
import { deprecate } from '@ember/debug';
import { EmberArray } from '@ember/-internals/utils';
import { arrayContentDidChange, arrayContentWillChange } from './array_events';
import { addListener, removeListener } from './events';
import { notifyPropertyChange } from './property_events';

const EMPTY_ARRAY = Object.freeze([]);

interface ObjectHasArrayObservers {
hasArrayObservers?: boolean;
}

export function objectAt<T>(array: T[] | EmberArray<T>, index: number): T | undefined {
if (Array.isArray(array)) {
return array[index];
Expand Down Expand Up @@ -56,84 +49,3 @@ export function replaceInNativeArray<T>(

arrayContentDidChange(array, start, deleteCount, items.length);
}

interface ArrayObserverOptions {
willChange?: string;
didChange?: string;
}

type Operation = (
obj: ObjectHasArrayObservers,
eventName: string,
target: object | Function | null,
callbackName: string
) => void;

function arrayObserversHelper(
obj: ObjectHasArrayObservers,
target: object | Function | null,
opts: ArrayObserverOptions | undefined,
operation: Operation,
notify: boolean
): ObjectHasArrayObservers {
let willChange = (opts && opts.willChange) || 'arrayWillChange';
let didChange = (opts && opts.didChange) || 'arrayDidChange';
let hasObservers = obj.hasArrayObservers;

operation(obj, '@array:before', target, willChange);
operation(obj, '@array:change', target, didChange);

if (hasObservers === notify) {
notifyPropertyChange(obj, 'hasArrayObservers');
}

return obj;
}

export function addArrayObserver<T>(
array: EmberArray<T>,
target: object | Function | null,
opts?: ArrayObserverOptions | undefined,
suppress = false
): ObjectHasArrayObservers {
deprecate(
`Array observers have been deprecated. Added an array observer to ${getDebugName?.(array)}.`,
suppress,
{
id: 'array-observers',
url: 'https://deprecations.emberjs.com/v3.x#toc_array-observers',
until: '4.0.0',
for: 'ember-source',
since: {
enabled: '3.26.0-beta.1',
},
}
);

return arrayObserversHelper(array, target, opts, addListener, false);
}

export function removeArrayObserver<T>(
array: EmberArray<T>,
target: object | Function | null,
opts?: ArrayObserverOptions | undefined,
suppress = false
): ObjectHasArrayObservers {
deprecate(
`Array observers have been deprecated. Removed an array observer from ${getDebugName?.(
array
)}.`,
suppress,
{
id: 'array-observers',
url: 'https://deprecations.emberjs.com/v3.x#toc_array-observers',
until: '4.0.0',
for: 'ember-source',
since: {
enabled: '3.26.0-beta.1',
},
}
);

return arrayObserversHelper(array, target, opts, removeListener, true);
}
5 changes: 0 additions & 5 deletions packages/@ember/-internals/metal/lib/array_events.ts
@@ -1,5 +1,4 @@
import { peekMeta } from '@ember/-internals/meta';
import { sendEvent } from './events';
import { notifyPropertyChange } from './property_events';

export function arrayContentWillChange<T extends object>(
Expand All @@ -22,8 +21,6 @@ export function arrayContentWillChange<T extends object>(
}
}

sendEvent(array, '@array:before', [array, startIdx, removeAmt, addAmt]);

return array;
}

Expand Down Expand Up @@ -58,8 +55,6 @@ export function arrayContentDidChange<T extends { length: number }>(
notifyPropertyChange(array, '[]', meta);
}

sendEvent(array, '@array:change', [array, startIdx, removeAmt, addAmt]);

if (meta !== null) {
let length = array.length;
let addedAmount = addAmt === -1 ? 0 : addAmt;
Expand Down
169 changes: 30 additions & 139 deletions packages/@ember/-internals/runtime/lib/mixins/array.js
Expand Up @@ -12,14 +12,10 @@ import {
replace,
computed,
Mixin,
hasListeners,
beginPropertyChanges,
endPropertyChanges,
addArrayObserver,
removeArrayObserver,
arrayContentWillChange,
arrayContentDidChange,
nativeDescDecorator as descriptor,
} from '@ember/-internals/metal';
import { assert } from '@ember/debug';
import Enumerable from './enumerable';
Expand Down Expand Up @@ -476,158 +472,53 @@ const ArrayMixin = Mixin.create(Enumerable, {
return -1;
},

// ..........................................................
// ARRAY OBSERVERS
//

/**
Adds an array observer to the receiving array. The array observer object
normally must implement two methods:

* `willChange(observedObj, start, removeCount, addCount)` - This method will be
called just before the array is modified.
* `didChange(observedObj, start, removeCount, addCount)` - This method will be
called just after the array is modified.

Both callbacks will be passed the observed object, starting index of the
change as well as a count of the items to be removed and added. You can use
these callbacks to optionally inspect the array during the change, clear
caches, or do any other bookkeeping necessary.

In addition to passing a target, you can also include an options hash
which you can use to override the method names that will be invoked on the
target.

@method addArrayObserver
@param {Object} target The observer object.
@param {Object} opts Optional hash of configuration options including
`willChange` and `didChange` option.
@return {EmberArray} receiver
@public
@example
import Service from '@ember/service';

export default Service.extend({
data: Ember.A(),

init() {
this._super(...arguments);

this.data.addArrayObserver(this, {
willChange: 'dataWillChange',
didChange: 'dataDidChange'
});
},

dataWillChange(array, start, removeCount, addCount) {
console.log('array will change', array, start, removeCount, addCount);
},

dataDidChange(array, start, removeCount, addCount) {
console.log('array did change', array, start, removeCount, addCount);
}
});
*/

addArrayObserver(target, opts) {
return addArrayObserver(this, target, opts);
},

/**
Removes an array observer from the object if the observer is current
registered. Calling this method multiple times with the same object will
have no effect.

@method removeArrayObserver
@param {Object} target The object observing the array.
@param {Object} opts Optional hash of configuration options including
`willChange` and `didChange` option.
@return {EmberArray} receiver
@public
*/
removeArrayObserver(target, opts) {
return removeArrayObserver(this, target, opts);
},

/**
Becomes true whenever the array currently has observers watching changes
on the array.

```javascript
let arr = [1, 2, 3, 4, 5];
arr.hasArrayObservers; // false

arr.addArrayObserver(this, {
willChange() {
console.log('willChange');
}
});
arr.hasArrayObservers; // true
```

@property {Boolean} hasArrayObservers
@public
*/
hasArrayObservers: descriptor({
configurable: true,
enumerable: false,
get() {
return hasListeners(this, '@array:change') || hasListeners(this, '@array:before');
},
}),

/**
If you are implementing an object that supports `EmberArray`, call this
method just before the array content changes to notify any observers and
invalidate any related properties. Pass the starting index of the change
as well as a delta of the amounts to change.

```app/components/show-post.js
import Component from '@ember/component';
import EmberObject from '@ember/object';
import Component from '@glimmer/component';
import EmberObject, { action } from '@ember/object';
import { tracked } from '@glimmer/tracking';

const Post = EmberObject.extend({
successfulModifications: 0,

body: '',
save() {}

length: computed(function() {
this.incrementProperty('successfulModifications');
}),
})

export default Component.extend({
attemptsToModify: 0,
successfulModifications: 0,
posts: null,
export default class ShowPost extends Component {
posts = null;

init() {
this._super(...arguments);
constructor() {
super(...arguments);

this.posts = [1, 2, 3].map(i => Post.create({ body: i }));
this.posts.addArrayObserver(this, {
willChange() {
this.incrementProperty('attemptsToModify');
},
didChange() {
this.incrementProperty('successfulModifications');
}
});
},

actions: {
editPost(post, newContent) {
let oldContent = post.body,
postIndex = this.posts.indexOf(post);

this.posts.arrayContentWillChange(postIndex, 0, 0); // attemptsToModify = 1
post.set('body', newContent);

post.save()
.then(response => {
this.posts.arrayContentDidChange(postIndex, 0, 0); // successfulModifications = 1
})
.catch(error => {
post.set('body', oldContent);
})
}
}
});

@action
editPost(post, newContent) {
let oldContent = post.body;
let postIndex = this.posts.indexOf(post);

post.body = newContent;

post.save()
.then(response => {
this.posts.arrayContentDidChange(postIndex, 0, 0); // post.successfulModifications = 1
})
.catch(error => {
post.body = oldContent;
})
}
}
```

@method arrayContentWillChange
Expand Down
26 changes: 24 additions & 2 deletions packages/@ember/-internals/runtime/lib/system/array_proxy.js
Expand Up @@ -7,11 +7,11 @@ import {
objectAt,
alias,
PROPERTY_DID_CHANGE,
addArrayObserver,
removeArrayObserver,
replace,
arrayContentDidChange,
tagForProperty,
addListener,
removeListener,
} from '@ember/-internals/metal';
import { isObject } from '@ember/-internals/utils';
import EmberObject from './object';
Expand All @@ -25,6 +25,28 @@ const ARRAY_OBSERVER_MAPPING = {
didChange: '_arrangedContentArrayDidChange',
};

// Mimic behaviour in old addArrayObserver, but smplified.
function addArrayObserver(obj, target, opts) {
let willChange = (opts && opts.willChange) || 'arrayWillChange';
let didChange = (opts && opts.didChange) || 'arrayDidChange';

addListener(obj, '@array:before', target, willChange);
addListener(obj, '@array:change', target, didChange);

return obj;
}

// Mimic behaviour in old addArrayObserver, but smplified.
function removeArrayObserver(obj, target, opts) {
let willChange = (opts && opts.willChange) || 'arrayWillChange';
let didChange = (opts && opts.didChange) || 'arrayDidChange';

removeListener(obj, '@array:before', target, willChange);
removeListener(obj, '@array:change', target, didChange);

return obj;
}

function customTagForArrayProxy(proxy, key) {
if (key === '[]') {
proxy._revalidate();
Expand Down