I'm trying to setup certain logic in Aurelia that would affect DOM nodes looped by repeat.for. If I understand the documentation correctly, the attached() callback of the view is called after DOM rendering and is the place put this kind of logic in.
The problem is that the attached() callback seems to be fired before the repeat.for binding is complete, leaving me with only a partially rendered dom.
In order to illustrate the problem:
I have a custom element containing:
<template>
<ul>
<li repeat.for="thing of things"></li>
</ul>
</template>
Once attached is called(), I would expect having a rendered DOM containing all the li elements, instead. A simple dump of the dom shows an empty
How can is implement a callback that gets access to those li nodes?
attached
is called when the component's DOM element is "attached" to the DOM. There may be child components such as repeat
ed templates that are further down in the queue to be rendered, the easiest thing to do would be to put your logic at the bottom of the queue:
import {inject, TaskQueue} from 'aurelia-framework';
@inject(TaskQueue)
export class MyComponent {
constructor(taskQueue) {
this.taskQueue = taskQueue;
}
doSomethingAfterRepeatIsRendered() {
// your logic...
}
attached() {
this.taskQueue.queueMicroTask({
call: () => this.doSomethingAfterRepeatIsRendered();
});
}
}
There are better approaches than this, but I would need to know more about what kind of work you need to do with the <li>
elements to provide a better answer. It's uncommon to use the TaskQueue directly, often things can be refactored into custom elements or attributes that participate in the composition lifecycle more naturally. For example, if you need to do some jQuery stuff with your <li>
elements, the "aurelia way" would be to separate this logic from your view-model using a custom attribute:
do-something.js
import {inject, customAttribute} from 'aurelia-framework';
import $ from 'jquery';
@customAttribute('do-something')
@inject(Element)
export class DoSomethingCustomAttribute {
constructor(element) {
// "element" will be the DOM element rendered from the
// template this attribute appears on, in this example an <li> element
this.element = element;
}
// now we don't need the task queue because attached is called when the
// <li> element is attached.
attached() {
// this.value is whatever the custom attribute is bound to, in this case
// its a "thing" from your "things" array.
$(this.element).doSomething(this.value);
}
}
Here's the usage:
app.html
<template>
<require from="./do-something"></require>
<ul>
<li repeat.for="thing of things" do-something.bind="thing"></li>
</ul>
</template>
Would like to add here another option for catching DOM changes that is pretty simple, works not only for aurelia and could be really useful when you have some dynamic DOM changes triggered on user interaction, you can use MutationObserver
https://developer.mozilla.org/en/docs/Web/API/MutationObserver
import {DOM} from 'aurelia-pal';
...
let mutationObserver = DOM.createMutationObserver(() => {
// handle dom changes here
});
//start to observe, note you can set different options
mutationObserver.observe(someDomElement, {childList: true, subtree: true, characterData: true});
when you don't neet to observe anymore, you do mutationObserver.disconnect();
usually from detached()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With