Reputation: 4024
How to check when DOM is compiled and inserted from Aurelia repeat cycle when the model is updated?
I have the following html:
<div clas="parent">
<div class="list-group">
<a repeat.for="$item of treeData">${$item.label}</a>
</div>
</div>
Here I need to know when all <a>
tags are listed in the DOM, in order to run jquery scroll plugin on the parent <div>
container.
At first load, I do that from the attached() method and all is fine.
When I update the treeData model from a listener, and try to update the jquery scroll plugin, it looks that the DOM is not compiled, so my scroll plugin can not update properly.
If I put timeout with some minimum value like 200ms it works, but I don't think it is a reliable workaround.
So is there a way to solve that?
Thanks!
My View Model:
@customElement('tree-view')
@inject(Element, ViewResources, BindingEngine)
export class TreeView {
@bindable data = [];
@bindable filterFunc = null;
@bindable filter = false;
@bindable selectedItem;
constructor(element, viewResources, bindingEngine) {
this.element = element;
this.viewResources = viewResources;
this.bindingEngine = bindingEngine;
}
bind(bindingContext, overrideContext) {
this.dataPropertySubscription = this.bindingEngine
.propertyObserver(this, 'data')
.subscribe((newItems, oldItems) => {
this.dataCollectionSubscription.dispose();
this._subscribeToDataCollectionChanges();
this.refresh();
});
this.refresh();
if (this.filter === true) {
this.filterChanged(this.filter);
}
if (this.selectedItem) {
this.selectedItemChanged(this.selectedItem);
}
}
attached() {
$(this.element).perfectScrollbar();
}
refresh() {
this.treeData = processData(this.data, this.filterFunc);
this.listItemMap = new WeakMap();
this.treeData.forEach(li => this.listItemMap.set(li.item, li));
this.filterChanged(this.filter);
$(this.element).perfectScrollbar('update');
}
This is only part of the code, but most valuable I think. I attach the jq plugin in attached function and try to update it in refresh function. In general I have listener that track model in other view, which then update that one without triggering bind method.
Upvotes: 1
Views: 364
Reputation: 11990
You could push your code onto the microTaskQueue, which will schedule your function to be executed on the next event loop. For instance:
import { TaskQueue } from 'aurelia-task-queue';
//...
@inject(Element, ViewResources, BindingEngine, TaskQueue)
export class TreeView {
constructor(element, viewResources, bindingEngine, taskQueue) {
this.element = element;
this.viewResources = viewResources;
this.bindingEngine = bindingEngine;
this.taskQueue = taskQueue;
}
refresh() {
this.treeData = processData(this.data, this.filterFunc);
this.listItemMap = new WeakMap();
this.treeData.forEach(li => this.listItemMap.set(li.item, li));
this.filterChanged(this.filter);
// queue another task, which will execute after the tasks queued above ^^^
this.taskQueue.queueMicroTask(() => {
$(this.element).perfectScrollbar('update');
});
}
}
Upvotes: 0
Reputation: 794
An approach would be to use something called window.requestAnimationFrame
(https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame).
In your view-model, when you modify your treeData
array, try calling
window.requestAnimationFrame(()=>{
$.fn.somePlugin();
});
Haven't tested this out, but based off what you're telling me, this might do what you need.
Upvotes: 1