diff --git a/packages/@ember/-internals/meta/lib/meta.ts b/packages/@ember/-internals/meta/lib/meta.ts index 2a60290e56c..9f38cb7f77c 100644 --- a/packages/@ember/-internals/meta/lib/meta.ts +++ b/packages/@ember/-internals/meta/lib/meta.ts @@ -792,7 +792,7 @@ export interface Meta { writeValues(subkey: string, value: any): void; peekValues(key: string): any; deleteFromValues(key: string): any; - readInheritedValue(key: string, subkey: string): any; + readInheritedValue(key: string): any; writeValue(obj: object, key: string, value: any): any; } @@ -808,34 +808,20 @@ if (DEBUG) { ); let map = this._getOrCreateOwnMap('_values'); - map[subkey] = value; + map[subkey] = value === undefined ? UNDEFINED : value; }; - Meta.prototype.peekValues = function(subkey: string) { - return this._findInherited2('_values', subkey); + Meta.prototype.peekValues = function(key: string) { + let val = this._findInherited2('_values', key); + return val === UNDEFINED ? undefined : val; }; - Meta.prototype.deleteFromValues = function(subkey: string) { - delete this._getOrCreateOwnMap('_values')[subkey]; + Meta.prototype.deleteFromValues = function(key: string) { + delete this._getOrCreateOwnMap('_values')[key]; }; - Meta.prototype.readInheritedValue = function(key, subkey) { - let internalKey = `_${key}`; - - let pointer: Meta | null = this; - - while (pointer !== null) { - let map = pointer[internalKey]; - if (map !== undefined) { - let value = map[subkey]; - if (value !== undefined || subkey in map) { - return value; - } - } - pointer = pointer.parent; - } - - return UNDEFINED; + Meta.prototype.readInheritedValue = function(key: string) { + return this._findInherited2('_values', key); }; Meta.prototype.writeValue = function(obj: object, key: string, value: any) { diff --git a/packages/@ember/-internals/metal/lib/properties.ts b/packages/@ember/-internals/metal/lib/properties.ts index 64bb57fff1c..04fd13ed9b9 100644 --- a/packages/@ember/-internals/metal/lib/properties.ts +++ b/packages/@ember/-internals/metal/lib/properties.ts @@ -56,10 +56,12 @@ export function INHERITING_GETTER_FUNCTION(name: string): InheritingGetterFuncti let meta = peekMeta(this); let val; if (meta !== null) { - val = meta.readInheritedValue('values', name); - if (val === UNDEFINED) { + val = meta.readInheritedValue(name); + if (val === undefined) { let proto = Object.getPrototypeOf(this); - return proto === null ? undefined : proto[name]; + val = proto === null ? undefined : proto[name]; + } else { + val = val === UNDEFINED ? undefined : val; } } diff --git a/packages/@ember/-internals/metal/lib/watch_key.ts b/packages/@ember/-internals/metal/lib/watch_key.ts index 8d07d1da1d0..dae271c3b20 100644 --- a/packages/@ember/-internals/metal/lib/watch_key.ts +++ b/packages/@ember/-internals/metal/lib/watch_key.ts @@ -1,4 +1,4 @@ -import { Meta, meta as metaFor, peekMeta, UNDEFINED } from '@ember/-internals/meta'; +import { Meta, meta as metaFor, peekMeta } from '@ember/-internals/meta'; import { lookupDescriptor } from '@ember/-internals/utils'; import { EMBER_METAL_TRACKED_PROPERTIES } from '@ember/canary-features'; import { DEBUG } from '@glimmer/env'; @@ -131,8 +131,8 @@ export function unwatchKey(obj: object, keyName: string, _meta?: Meta): void { maybeMandatoryDescriptor.get && (maybeMandatoryDescriptor.get as InheritingGetterFunction).isInheritingGetter ) { - let possibleValue = meta.readInheritedValue('values', keyName); - if (possibleValue === UNDEFINED) { + let possibleValue = meta.readInheritedValue(keyName); + if (possibleValue === undefined) { delete obj[keyName]; return; } diff --git a/packages/@ember/-internals/runtime/tests/system/object/observer_test.js b/packages/@ember/-internals/runtime/tests/system/object/observer_test.js index cc7125346a0..7efa16d8a2d 100644 --- a/packages/@ember/-internals/runtime/tests/system/object/observer_test.js +++ b/packages/@ember/-internals/runtime/tests/system/object/observer_test.js @@ -24,6 +24,31 @@ moduleFor( assert.equal(get(obj, 'count'), 1, 'should invoke observer after change'); } + async ['@test setting `undefined` value on observed property behaves correctly'](assert) { + let MyClass = EmberObject.extend({ + mood: 'good', + foo: observer('mood', function() {}), + }); + + let obj = MyClass.create(); + assert.equal(get(obj, 'mood'), 'good'); + + set(obj, 'mood', 'bad'); + await runLoopSettled(); + + assert.equal(get(obj, 'mood'), 'bad'); + + set(obj, 'mood', undefined); + await runLoopSettled(); + + assert.equal(get(obj, 'mood'), undefined); + + set(obj, 'mood', 'awesome'); + await runLoopSettled(); + + assert.equal(get(obj, 'mood'), 'awesome'); + } + async ['@test observer on subclass'](assert) { let MyClass = EmberObject.extend({ count: 0,