This addon was created as a rethinking of ember-ref-modifier, with a more simplified API and without some of the downsides of the previous implementation.
The addon allows users to get access to DOM nodes inside components, including accessing wrapping/destroying logic.
A simple use case:
- applying
ref
modifier with passed name to an element.
- gain access to it inside the component class as a decorated property
import Component from '@glimmer/component';
import { ref } from 'ember-ref-bucket';
export default class MyComponent extends Component {
@ref("FavouriteNode") node;
// this.node === "<div>hello</div>"
}
API differences, comparing to ember-ref-modifier
:
In ember-ref-modifier
ref modifier accept 2 positional arguments {{ref this "property"}}
:
- context to set path (
this
) - path to set on context (
"property"
)
In ember-ref-bucket
ref modifier accept 1 positional argument {{create-ref "field"}}
:
- reference name (
"field"
)
reference name should be passed as an argument to the @ref("field")
decorator, to allow it to find the reference by name.
- Ember.js v3.24 or above
- Ember CLI v3.24 or above
- Node.js v14 or above
ember install ember-ref-bucket
import Component from '@glimmer/component';
import { ref } from 'ember-ref-bucket';
import { action } from '@ember/object';
export class Player extends Component {
@ref('player') audioNode;
@action onPlay() {
this.audioNode.play()
}
}
import Component from '@glimmer/component';
import { ref } from 'ember-ref-bucket';
export default class MyComponent extends Component {
@ref("field") node = null;
}
This method is very useful if you want to wrap the node and control its lifecycle.
import Component from '@glimmer/component';
import { ref, registerNodeDestructor } from 'ember-ref-bucket';
class NodeWrapper {
constructor(node) {
this.node = node;
}
destroy() {
this.node = null;
}
value() {
return this.node.textContent;
}
}
export default class WrappedNodeComponent extends Component {
@ref('field', (node) => {
const instance = new NodeWrapper(node);
registerNodeDestructor(node, () => instance.destroy());
return instance;
}) node = null;
get value() {
return this.node?.value();
}
}
import { ref, globalRef, trackedRef, trackedGlobalRef } from 'ember-ref-bucket';
/*
ref - usage: @ref('foo', nodeWrapFn?), ref to bucket with current component context
globalRef - usage: @globalRef('foo', nodeWrapFn?), ref to global context (app)
trackedRef - usage: @trackedRef('foo', nodeWrapFn?), tracked ref to local context
trackedGlobalRef - usage: @trackedGlobalRef('foo', nodeWrapFn?), tracked ref to global context (app)
*/
import { registerNodeDestructor, unregisterNodeDestructor } from 'ember-ref-bucket';
/*
registerNodeDestructor(node, fn) - to assign any ref-node destructor
unregisterNodeDestructor(node, fn) - to remove assigned ref-node destructor
usage will be like:
@ref('field', (node) => {
const item = new InputMask(node);
registerNodeDestructor(node, () => item.destroy());
return item;
});
*/
/*
nodeFor - functional low-level primitive to get node access
*/
import { nodeFor } from 'ember-ref-bucket';
const domNode = nodeFor(this, 'field');
-
If you use dom node in
@tracked
chain calculations, you should usetrackedRef
. -
If you don't need to rerun the tracked chain (for example, you use
ref
only for some event-based dom access), you should not usetrackedRef
.
- If you need to watch for node changes (resize, content, attributes), you can use the
create-tracked-ref
modifier. It can add observe resizing and mutations for the associated element and will mark it as "dirty" for any mutation.
Options:
resize
- default:false
, if truthy observes the resizing of the DOM element.attributes
- default:false
, if truthy observes the changing of any attribute on the DOM element.character
- default:false
, if truthy observes the change of the innerText of the DOM element. Note that setting innerText can change the children or the character depending on the current content of the element.children
- default:false
, if truthy observes changes to the list of direct children of the DOM element.subtree
- default:false
, if truthy observes the above options on the entire DOM subtree, not just the element decorated by the modifier.
- If you need to recalculate helper if some dom node changes (size, children, attributes), you need to use
tracked-ref-to
helper. - If you don't need it (you need to just have ref to dom node), you should choose
ref-to
helper.
create-ref
modifier andref-to
helpers will not work in template-only components (because of no context). You should usecreate-global-ref
andglobal-ref-to
instead. You can also provide abucket
param to thecreate-ref
modifier / helper.
The addon provide only 1 modifier (create-ref
) and 1 helper (ref-to
). Other derivatives will be transformed, and are described below:
Invocation | Will be transformed to |
---|---|
{{create-ref "foo"}} |
{{create-ref "foo" bucket=this}} |
{{create-tracked-ref "foo"}} |
{{create-ref "foo" bucket=this tracked=true}} |
{{create-global-ref "foo"}} |
{{create-ref "foo" bucket=undefined}} |
{{create-tracked-global-ref "foo"}} |
{{create-ref "foo" bucket=undefined tracked=true}} |
Invocation | Will be transformed to |
---|---|
{{ref-to "foo"}} |
{{ref-to "foo" bucket=this}} |
{{tracked-ref-to "foo"}} |
{{ref-to "foo" bucket=this tracked=true}} |
{{global-ref-to "foo"}} |
{{ref-to "foo" bucket=undefined}} |
{{tracked-global-ref-to "foo"}} |
{{ref-to "foo" bucket=undefined tracked=true}} |
See the Contributing guide for details.
Version matrix:
Ember-Modifier 4 - v5; Ember 3.28 - v4; Ember 3.24 - v3
This project is licensed under the MIT License.