ember-ref-bucket
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.
Compatibility
- Ember.js v3.24 or above
- Ember CLI v3.24 or above
- Node.js v14 or above
Installation
ember install ember-ref-bucket
Usage
Examples
Simple player
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()
}
}
div
to node
property.
Link import Component from '@glimmer/component';
import { ref } from 'ember-ref-bucket';
export default class MyComponent extends Component {
@ref("field") node = null;
}
div
content updates
Dynamically show div
as component argument
Use registerNodeDestructor
Use 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();
}
}
Available decorators:
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)
*/
Available methods:
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');
@trackedRef
decorators
Definition of -
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
.
{{create-tracked-ref}}
modifiers
Definition of - 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.
{{tracked-ref-to}}
helpers
Definition of - 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.
Template-only components
-
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:
Modifiers will be transformed according to this table:
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}} |
Helpers will be transformed according to this table:
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}} |
Contributing
See the Contributing guide for details.
Version matrix:
Ember-Modifier 4 - v5; Ember 3.28 - v4; Ember 3.24 - v3
License
This project is licensed under the MIT License.