diff --git a/packages/@ember/-internals/metal/index.ts b/packages/@ember/-internals/metal/index.ts index 29417e57e85..c6f731aee75 100644 --- a/packages/@ember/-internals/metal/index.ts +++ b/packages/@ember/-internals/metal/index.ts @@ -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'; diff --git a/packages/@ember/-internals/metal/lib/array.ts b/packages/@ember/-internals/metal/lib/array.ts index 92043a6128e..b1e866049dc 100644 --- a/packages/@ember/-internals/metal/lib/array.ts +++ b/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(array: T[] | EmberArray, index: number): T | undefined { if (Array.isArray(array)) { return array[index]; @@ -56,84 +49,3 @@ export function replaceInNativeArray( 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( - array: EmberArray, - 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( - array: EmberArray, - 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); -} diff --git a/packages/@ember/-internals/metal/lib/array_events.ts b/packages/@ember/-internals/metal/lib/array_events.ts index ee7c73285c5..56e160c75da 100644 --- a/packages/@ember/-internals/metal/lib/array_events.ts +++ b/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( @@ -22,8 +21,6 @@ export function arrayContentWillChange( } } - sendEvent(array, '@array:before', [array, startIdx, removeAmt, addAmt]); - return array; } @@ -58,8 +55,6 @@ export function arrayContentDidChange( notifyPropertyChange(array, '[]', meta); } - sendEvent(array, '@array:change', [array, startIdx, removeAmt, addAmt]); - if (meta !== null) { let length = array.length; let addedAmount = addAmt === -1 ? 0 : addAmt; diff --git a/packages/@ember/-internals/runtime/lib/mixins/array.js b/packages/@ember/-internals/runtime/lib/mixins/array.js index d934edc8ba9..6ac0376e20a 100644 --- a/packages/@ember/-internals/runtime/lib/mixins/array.js +++ b/packages/@ember/-internals/runtime/lib/mixins/array.js @@ -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'; @@ -476,106 +472,6 @@ 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 @@ -583,51 +479,46 @@ const ArrayMixin = Mixin.create(Enumerable, { 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 diff --git a/packages/@ember/-internals/runtime/lib/system/array_proxy.js b/packages/@ember/-internals/runtime/lib/system/array_proxy.js index 8fed955c0a9..d099c79ced4 100644 --- a/packages/@ember/-internals/runtime/lib/system/array_proxy.js +++ b/packages/@ember/-internals/runtime/lib/system/array_proxy.js @@ -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'; @@ -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(); diff --git a/packages/@ember/-internals/runtime/tests/helpers/array.js b/packages/@ember/-internals/runtime/tests/helpers/array.js index 0f5f5008d38..737c8a3b63b 100644 --- a/packages/@ember/-internals/runtime/tests/helpers/array.js +++ b/packages/@ember/-internals/runtime/tests/helpers/array.js @@ -4,8 +4,6 @@ import { generateGuid, guidFor } from '@ember/-internals/utils'; import { get, computed, - addArrayObserver, - removeArrayObserver, arrayContentWillChange, arrayContentDidChange, } from '@ember/-internals/metal'; @@ -60,16 +58,6 @@ const ArrayTestsObserverClass = EmberObject.extend({ return this; }, - observeArray(obj) { - addArrayObserver(obj, this); - return this; - }, - - stopObserveArray(obj) { - removeArrayObserver(obj, this); - return this; - }, - propertyDidChange(target, key, value) { if (this._keys[key] === undefined) { this._keys[key] = 0; diff --git a/packages/@ember/-internals/runtime/tests/mixins/array_test.js b/packages/@ember/-internals/runtime/tests/mixins/array_test.js index 49b158919ae..47214246e47 100644 --- a/packages/@ember/-internals/runtime/tests/mixins/array_test.js +++ b/packages/@ember/-internals/runtime/tests/mixins/array_test.js @@ -5,8 +5,6 @@ import { addObserver, observer as emberObserver, computed, - addArrayObserver, - removeArrayObserver, arrayContentDidChange, arrayContentWillChange, } from '@ember/-internals/metal'; @@ -196,8 +194,6 @@ moduleFor( 'notify array observers', class extends AbstractTestCase { beforeEach(assert) { - expectDeprecation(/Array observers have been deprecated/); - obj = DummyArray.create(); observer = EmberObject.extend({ @@ -214,8 +210,6 @@ moduleFor( _before: null, _after: null, }); - - addArrayObserver(obj, observer); } afterEach() { @@ -246,31 +240,6 @@ moduleFor( arrayContentDidChange(obj, 0, 2, 1); assert.deepEqual(observer._after, [obj, 0, 2, 1]); } - - ['@test removing array observer should disable'](assert) { - removeArrayObserver(obj, observer); - arrayContentWillChange(obj); - assert.deepEqual(observer._before, null); - - arrayContentDidChange(obj); - assert.deepEqual(observer._after, null); - } - - ['@test hasArrayObservers should work'](assert) { - assert.equal( - obj.hasArrayObservers, - true, - 'correctly shows it has an array observer when one exists' - ); - - removeArrayObserver(obj, observer); - - assert.equal( - obj.hasArrayObservers, - false, - 'correctly shows it has an array observer when one exists' - ); - } } ); diff --git a/packages/@ember/-internals/runtime/tests/mutable-array/replace-test.js b/packages/@ember/-internals/runtime/tests/mutable-array/replace-test.js index afa44afd6f2..8d684149895 100644 --- a/packages/@ember/-internals/runtime/tests/mutable-array/replace-test.js +++ b/packages/@ember/-internals/runtime/tests/mutable-array/replace-test.js @@ -241,8 +241,6 @@ class ReplaceTests extends AbstractTestCase { } async '@test Adding object should notify array observer'() { - expectDeprecation(/Array observers have been deprecated/); - let fixtures = newFixture(4); let obj = this.newObject(fixtures); let observer = this.newObserver(obj).observeArray(obj); diff --git a/packages/@ember/-internals/runtime/tests/system/array_proxy/array_observer_test.js b/packages/@ember/-internals/runtime/tests/system/array_proxy/array_observer_test.js deleted file mode 100644 index 8515d192521..00000000000 --- a/packages/@ember/-internals/runtime/tests/system/array_proxy/array_observer_test.js +++ /dev/null @@ -1,55 +0,0 @@ -import { set } from '@ember/-internals/metal'; -import ArrayProxy from '../../../lib/system/array_proxy'; -import { A } from '../../../lib/mixins/array'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; - -moduleFor( - 'ArrayProxy - array observers', - class extends AbstractTestCase { - ['@test mutating content'](assert) { - expectDeprecation(/Array observers have been deprecated/); - - assert.expect(5); - - let content = A(['x', 'y', 'z']); - let proxy = ArrayProxy.create({ content }); - - proxy.addArrayObserver({ - arrayWillChange(proxy, startIndex, removeCount, addCount) { - assert.deepEqual([startIndex, removeCount, addCount], [1, 1, 3]); - assert.deepEqual(proxy.toArray(), ['x', 'y', 'z']); - }, - arrayDidChange(proxy, startIndex, removeCount, addCount) { - assert.deepEqual([startIndex, removeCount, addCount], [1, 1, 3]); - assert.deepEqual(proxy.toArray(), ['x', 'a', 'b', 'c', 'z']); - }, - }); - - proxy.toArray(); - content.replace(1, 1, ['a', 'b', 'c']); - } - - ['@test assigning content'](assert) { - expectDeprecation(/Array observers have been deprecated/); - - assert.expect(5); - - let content = A(['x', 'y', 'z']); - let proxy = ArrayProxy.create({ content }); - - proxy.addArrayObserver({ - arrayWillChange(proxy, startIndex, removeCount, addCount) { - assert.deepEqual([startIndex, removeCount, addCount], [0, 3, 5]); - assert.deepEqual(proxy.toArray(), ['x', 'y', 'z']); - }, - arrayDidChange(proxy, startIndex, removeCount, addCount) { - assert.deepEqual([startIndex, removeCount, addCount], [0, 3, 5]); - assert.deepEqual(proxy.toArray(), ['a', 'b', 'c', 'd', 'e']); - }, - }); - - proxy.toArray(); - set(proxy, 'content', A(['a', 'b', 'c', 'd', 'e'])); - } - } -); diff --git a/packages/@ember/-internals/runtime/tests/system/array_proxy/watching_and_listening_test.js b/packages/@ember/-internals/runtime/tests/system/array_proxy/watching_and_listening_test.js deleted file mode 100644 index 0717a429bd7..00000000000 --- a/packages/@ember/-internals/runtime/tests/system/array_proxy/watching_and_listening_test.js +++ /dev/null @@ -1,66 +0,0 @@ -import { peekMeta } from '@ember/-internals/meta'; -import ArrayProxy from '../../../lib/system/array_proxy'; -import { A } from '../../../lib/mixins/array'; -import { moduleFor, AbstractTestCase } from 'internal-test-helpers'; - -function sortedListenersFor(obj, eventName) { - let listeners = peekMeta(obj).matchingListeners(eventName) || []; - - let keys = []; - for (let i = 0; i < listeners.length; i += 3) { - keys.push(listeners[i + 1]); - } - return keys.sort(); -} - -moduleFor( - 'ArrayProxy - watching and listening', - class extends AbstractTestCase { - [`@test setting 'content' adds listeners correctly`](assert) { - let content = A(); - let proxy = ArrayProxy.create(); - - assert.deepEqual(sortedListenersFor(content, '@array:before'), []); - assert.deepEqual(sortedListenersFor(content, '@array:change'), []); - - proxy.set('content', content); - - assert.deepEqual(sortedListenersFor(content, '@array:before'), [ - '_arrangedContentArrayWillChange', - ]); - assert.deepEqual(sortedListenersFor(content, '@array:change'), [ - '_arrangedContentArrayDidChange', - ]); - } - - [`@test changing 'content' adds and removes listeners correctly`](assert) { - let content1 = A(); - let content2 = A(); - let proxy = ArrayProxy.create({ content: content1 }); - - assert.deepEqual(sortedListenersFor(content1, '@array:before'), []); - assert.deepEqual(sortedListenersFor(content1, '@array:change'), []); - - // setup proxy - proxy.length; - - assert.deepEqual(sortedListenersFor(content1, '@array:before'), [ - '_arrangedContentArrayWillChange', - ]); - assert.deepEqual(sortedListenersFor(content1, '@array:change'), [ - '_arrangedContentArrayDidChange', - ]); - - proxy.set('content', content2); - - assert.deepEqual(sortedListenersFor(content1, '@array:before'), []); - assert.deepEqual(sortedListenersFor(content1, '@array:change'), []); - assert.deepEqual(sortedListenersFor(content2, '@array:before'), [ - '_arrangedContentArrayWillChange', - ]); - assert.deepEqual(sortedListenersFor(content2, '@array:change'), [ - '_arrangedContentArrayDidChange', - ]); - } - } -); diff --git a/tests/docs/expected.js b/tests/docs/expected.js index 54ac5a51513..d77b7f1c46d 100644 --- a/tests/docs/expected.js +++ b/tests/docs/expected.js @@ -58,7 +58,6 @@ module.exports = { 'actions', 'activate', 'adapter', - 'addArrayObserver', 'addListener', 'addObject', 'addObjects', @@ -248,7 +247,6 @@ module.exports = { 'handleEvent', 'handleURL', 'has', - 'hasArrayObservers', 'has-block', 'has-block-params', 'hash', @@ -418,7 +416,6 @@ module.exports = { 'reject', 'rejectBy', 'releaseMethods', - 'removeArrayObserver', 'removeAt', 'removeListener', 'removeObject',