diff --git a/addon/index.js b/addon/index.js index 7d3a8b4..943768f 100644 --- a/addon/index.js +++ b/addon/index.js @@ -4,7 +4,7 @@ export { registerNodeDestructor, unregisterNodeDestructor, } from "./utils/ref"; - +import { addPrototypeReference } from "./utils/prototype-reference"; export function nodeFor(context, name) { return bucketFor(context).get(name); @@ -14,7 +14,7 @@ function maybeReturnCreated(value, createdValues, fn, ctx) { if (value === null || value === undefined) { return null; } - if (fn) { + if (typeof fn === 'function') { if (!createdValues.has(value)) { createdValues.set(value, fn.call(ctx, value)); } @@ -24,9 +24,14 @@ function maybeReturnCreated(value, createdValues, fn, ctx) { } } + + export function ref(name, fn) { - return function () { + return function (klass, objectKey) { const createdValues = new WeakMap(); + if (typeof fn === 'function') { + addPrototypeReference(klass, objectKey, name); + } return { get() { const value = bucketFor(this).get(name); @@ -37,8 +42,11 @@ export function ref(name, fn) { } export function globalRef(name, fn) { - return function () { + return function (klass, objectKey) { const createdValues = new WeakMap(); + if (typeof fn === 'function') { + addPrototypeReference(klass, objectKey, name); + } return { get() { const value = bucketFor(getOwner(this) || resolveGlobalRef()).get(name); @@ -49,8 +57,11 @@ export function globalRef(name, fn) { } export function trackedRef(name, fn) { - return function () { + return function (klass, objectKey) { const createdValues = new WeakMap(); + if (typeof fn === 'function') { + addPrototypeReference(klass, objectKey, name); + } return { get() { const value = bucketFor(this).getTracked(name); @@ -61,8 +72,11 @@ export function trackedRef(name, fn) { } export function trackedGlobalRef(name, fn) { - return function () { + return function (klass, objectKey) { const createdValues = new WeakMap(); + if (typeof fn === 'function') { + addPrototypeReference(klass, objectKey, name); + } return { get() { const value = bucketFor(getOwner(this) || resolveGlobalRef()).getTracked(name); diff --git a/addon/modifiers/create-ref.js b/addon/modifiers/create-ref.js index 8a870ef..129770f 100644 --- a/addon/modifiers/create-ref.js +++ b/addon/modifiers/create-ref.js @@ -3,8 +3,8 @@ import { getOwner } from "@ember/application"; import { action } from "@ember/object"; import { assert } from "@ember/debug"; -import { setGlobalRef, bucketFor, getNodeDestructors } from "./../utils/ref"; - +import { setGlobalRef, bucketFor, getNodeDestructors, watchFor } from "./../utils/ref"; +import { getReferencedKeys } from "../utils/prototype-reference"; export default class RefModifier extends Modifier { _key = this.name; _ctx = this.ctx; @@ -98,6 +98,13 @@ export default class RefModifier extends Modifier { } this._ctx = this.ctx; this._key = this.name; + watchFor(this.name, this.ctx, () => { + const keys = getReferencedKeys(this.ctx, this.name); + keys.forEach((keyName) => { + // consume keys with callbacks + this.ctx[keyName]; + }) + }); bucketFor(this.ctx).add(this.name, this.element); if (this.isTracked) { this.installMutationObservers(); diff --git a/addon/utils/prototype-reference.js b/addon/utils/prototype-reference.js new file mode 100644 index 0000000..0f016bd --- /dev/null +++ b/addon/utils/prototype-reference.js @@ -0,0 +1,27 @@ +const PrototypeReferences = new WeakMap(); + +export function addPrototypeReference(klass, objectKey, referenceName) { + if (!PrototypeReferences.has(klass)) { + PrototypeReferences.set(klass, {}); + } + let obj = PrototypeReferences.get(klass); + if (!(referenceName in obj)) { + obj[referenceName] = new Set(); + } + obj[referenceName].add(objectKey); +} + +export function getReferencedKeys(klassInstance, referenceName) { + let proto = klassInstance; + while (proto.__proto__) { + proto = proto.__proto__; + if (PrototypeReferences.has(proto)) { + let maybeData = PrototypeReferences.get(proto); + if (referenceName in maybeData) { + return Array.from(maybeData[referenceName]); + } + } + } + + return []; +} diff --git a/tests/dummy/app/controllers/application.js b/tests/dummy/app/controllers/application.js index 25b5e8a..d303ccb 100644 --- a/tests/dummy/app/controllers/application.js +++ b/tests/dummy/app/controllers/application.js @@ -1,5 +1,5 @@ import Controller from '@ember/controller'; -import { trackedRef, registerNodeDestructor } from 'ember-ref-bucket'; +import { trackedRef, registerNodeDestructor, ref } from 'ember-ref-bucket'; class NodeWrapper { constructor(node) { @@ -22,4 +22,9 @@ export default class ApplicationController extends Controller { get value() { return this.node?.value(); } + @ref('bar', function(node) { + const instance = new NodeWrapper(node); + registerNodeDestructor(node, () => instance.destroy()); + return instance; + }) barNode; } diff --git a/tests/dummy/app/templates/application.hbs b/tests/dummy/app/templates/application.hbs index 30eccd3..df85b0a 100644 --- a/tests/dummy/app/templates/application.hbs +++ b/tests/dummy/app/templates/application.hbs @@ -1,7 +1,7 @@