-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #18069 from emberjs/tracked/dependent-key-compat
[FEATURE] Adds dependentKeyCompat
- Loading branch information
Showing
5 changed files
with
240 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
import { Meta } from '@ember/-internals/meta'; | ||
import { | ||
consume, | ||
Decorator, | ||
DecoratorPropertyDescriptor, | ||
isElementDescriptor, | ||
setClassicDecorator, | ||
tagForProperty, | ||
track, | ||
update, | ||
} from '@ember/-internals/metal'; | ||
import { EMBER_METAL_TRACKED_PROPERTIES } from '@ember/canary-features'; | ||
import { assert } from '@ember/debug'; | ||
|
||
let wrapGetterSetter = function(_target: object, key: string, desc: PropertyDescriptor) { | ||
let { get: originalGet } = desc; | ||
|
||
if (originalGet !== undefined) { | ||
desc.get = function() { | ||
let propertyTag = tagForProperty(this, key); | ||
let ret; | ||
|
||
let tag = track(() => { | ||
ret = originalGet!.call(this); | ||
}); | ||
|
||
update(propertyTag, tag); | ||
consume(tag); | ||
|
||
return ret; | ||
}; | ||
} | ||
|
||
return desc; | ||
}; | ||
|
||
export function dependentKeyCompat( | ||
target: object, | ||
key: string, | ||
desc: PropertyDescriptor | ||
): PropertyDescriptor; | ||
export function dependentKeyCompat(desc: { get?: Function; set?: Function }): Decorator; | ||
export function dependentKeyCompat( | ||
target: object | { get?: Function; set?: Function }, | ||
key?: string, | ||
desc?: PropertyDescriptor | ||
) { | ||
assert( | ||
'The dependentKeyCompat decorator can only be used if the tracked properties feature is enabled', | ||
Boolean(EMBER_METAL_TRACKED_PROPERTIES) | ||
); | ||
|
||
if (!isElementDescriptor([target, key, desc])) { | ||
desc = target as PropertyDescriptor; | ||
|
||
let decorator = function( | ||
target: object, | ||
key: string, | ||
_desc: DecoratorPropertyDescriptor, | ||
_meta?: Meta, | ||
isClassicDecorator?: boolean | ||
) { | ||
assert( | ||
'The @dependentKeyCompat decorator may only be passed a method when used in classic classes. You should decorate getters/setters directly in native classes', | ||
isClassicDecorator | ||
); | ||
|
||
assert( | ||
'The dependentKeyCompat() decorator must be passed a getter or setter when used in classic classes', | ||
desc !== null && | ||
typeof desc === 'object' && | ||
(typeof desc.get === 'function' || typeof desc.set === 'function') | ||
); | ||
|
||
return wrapGetterSetter(target, key, desc!); | ||
}; | ||
|
||
setClassicDecorator(decorator); | ||
|
||
return decorator as Decorator; | ||
} | ||
|
||
assert( | ||
'The @dependentKeyCompat decorator must be applied to getters/setters when used in native classes', | ||
(desc !== null && typeof desc!.get === 'function') || typeof desc!.set === 'function' | ||
); | ||
|
||
return wrapGetterSetter(target, key!, desc!); | ||
} | ||
|
||
setClassicDecorator(dependentKeyCompat as Decorator); |
126 changes: 126 additions & 0 deletions
126
packages/@ember/object/tests/computed/dependent-key-compat-test.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { EMBER_METAL_TRACKED_PROPERTIES } from '@ember/canary-features'; | ||
|
||
import { Object as EmberObject } from '@ember/-internals/runtime'; | ||
import { computed, tracked, observer } from '@ember/-internals/metal'; | ||
import { dependentKeyCompat } from '../../compat'; | ||
import { moduleFor, AbstractTestCase, runLoopSettled } from 'internal-test-helpers'; | ||
|
||
if (EMBER_METAL_TRACKED_PROPERTIES) { | ||
moduleFor( | ||
'dependentKeyCompat', | ||
class extends AbstractTestCase { | ||
'@test it works with computed properties'(assert) { | ||
class Person { | ||
@tracked firstName = 'Tom'; | ||
@tracked lastName = 'Dale'; | ||
|
||
@dependentKeyCompat | ||
get givenName() { | ||
return this.firstName; | ||
} | ||
|
||
@computed('givenName', 'lastName') | ||
get fullName() { | ||
return `${this.givenName} ${this.lastName}`; | ||
} | ||
} | ||
|
||
let tom = new Person(); | ||
|
||
assert.equal(tom.fullName, 'Tom Dale'); | ||
|
||
tom.firstName = 'Thomas'; | ||
|
||
assert.equal(tom.fullName, 'Thomas Dale'); | ||
} | ||
|
||
'@test it works with classic classes'(assert) { | ||
let Person = EmberObject.extend({ | ||
firstName: tracked({ value: 'Tom' }), | ||
lastName: tracked({ value: 'Dale' }), | ||
|
||
givenName: dependentKeyCompat({ | ||
get() { | ||
return this.firstName; | ||
}, | ||
}), | ||
|
||
fullName: computed('givenName', 'lastName', function() { | ||
return `${this.givenName} ${this.lastName}`; | ||
}), | ||
}); | ||
|
||
let tom = Person.create(); | ||
|
||
assert.equal(tom.fullName, 'Tom Dale'); | ||
|
||
tom.firstName = 'Thomas'; | ||
|
||
assert.equal(tom.fullName, 'Thomas Dale'); | ||
} | ||
|
||
async '@test it works with async observers'(assert) { | ||
let count = 0; | ||
|
||
let Person = EmberObject.extend({ | ||
firstName: tracked({ value: 'Tom' }), | ||
lastName: tracked({ value: 'Dale' }), | ||
|
||
givenName: dependentKeyCompat({ | ||
get() { | ||
return this.firstName; | ||
}, | ||
}), | ||
|
||
givenNameObserver: observer({ | ||
dependentKeys: ['givenName'], | ||
fn() { | ||
count++; | ||
}, | ||
sync: false, | ||
}), | ||
}); | ||
|
||
let tom = Person.create(); | ||
|
||
assert.equal(count, 0); | ||
|
||
tom.firstName = 'Thomas'; | ||
await runLoopSettled(); | ||
|
||
assert.equal(count, 1); | ||
} | ||
|
||
'@test it does not work with sync observers'(assert) { | ||
let count = 0; | ||
|
||
let Person = EmberObject.extend({ | ||
firstName: tracked({ value: 'Tom' }), | ||
lastName: tracked({ value: 'Dale' }), | ||
|
||
givenName: dependentKeyCompat({ | ||
get() { | ||
return this.firstName; | ||
}, | ||
}), | ||
|
||
givenNameObserver: observer({ | ||
dependentKeys: ['givenName'], | ||
fn() { | ||
count++; | ||
}, | ||
sync: true, | ||
}), | ||
}); | ||
|
||
let tom = Person.create(); | ||
|
||
assert.equal(count, 0); | ||
|
||
tom.firstName = 'Thomas'; | ||
|
||
assert.equal(count, 0); | ||
} | ||
} | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters