pragma-views2
Version:
188 lines (154 loc) • 6.69 kB
JavaScript
import { BehaviourBase } from "./behaviour-base.js";
import { parseElements } from "../binding/dom-parsing.js";
import { releaseElement, releaseElements } from "../binding/binding-helpers.js";
import { addObserverTrigger, removeObserversFromCache, ArrayObserver, observe } from "../binding/observers.js";
import { PropertyBehaviour } from "./property-behaviour.js";
import { postProcessPropertyChange } from "./behaviour-postprocess.js";
import {getValueOnPath} from "../objectpath-helper.js";
/**
* JHR: todo
* Need to refactor this so that you have one behaviour for arrays and one for datasources
*/
export class RepeatBehaviour extends BehaviourBase {
static create(attr, bindingContext) {
const result = new RepeatBehaviour();
result._context = bindingContext;
result._property = attr.nodeValue;
result._parentElement = attr.ownerElement.parentElement;
result.connectedCallback(attr.ownerElement);
attr.ownerElement.__behaviors.push(result);
}
connectedCallback(element) {
this._lastId = -1;
this.dataSourceChangedHandler = this.dataSourceChanged.bind(this);
this.collectionChangedHandler = this.collectionChanged.bind(this);
this.itemsAddedHandler = this._itemsAdded.bind(this);
this.itemsRemovedHandler = this._itemsRemoved.bind(this);
super.connectedCallback(element);
addObserverTrigger(this._context, this._property, this.collectionChangedHandler);
const targetObject = getValueOnPath(this._context, this._property);
if (targetObject != undefined) {
if (targetObject.addRootItemsLoadedCallback != undefined) {
targetObject.addRootItemsLoadedCallback(this.dataSourceChanged.bind(this));
}
}
}
disconnectedCallback() {
super.disconnectedCallback();
if (this._parentElement != undefined) {
this._removeAllChildren();
}
this._context = null;
this._property = null;
this._parentElement = null;
this.itemsAddedHandler = null;
this.itemsRemovedHandler = null;
}
collectionChanged(newValue, oldValue) {
if (this.suspendChangeEvent == true) return;
if (oldValue != undefined && oldValue.removeRootItemsLoadedCallback != undefined) {
this._removeAllChildren();
oldValue.removeRootItemsLoadedCallback(this.dataSourceChangedHandler);
}
if (newValue.addRootItemsLoadedCallback != undefined) {
newValue.addRootItemsLoadedCallback(this.dataSourceChangedHandler);
this.dataSourceChanged(newValue);
return;
}
this._removeAllChildren();
if (typeof oldValue == ArrayObserver) {
oldValue.unregisterAddCallback(this.itemsAddedHandler);
oldValue.unregisterRemovedCallback(this.itemsRemovedHandler);
removeObserversFromCache(oldValue);
}
this.suspendChangeEvent = true;
let observer;
if (this._context[this._property] instanceof ArrayObserver) {
observer = this._context[this._property];
}
else
{
for (let i = 0; i < newValue.length; i++) {
newValue[i] = observe(newValue[i]);
}
observer = this._context[this._property] = new ArrayObserver(this._context, this._property);
}
observer.registerAddCallback(this.itemsAddedHandler);
observer.registerRemovedCallback(this.itemsRemovedHandler);
this._itemsAdded(newValue);
this.suspendChangeEvent = false;
}
async _itemsRemoved(items) {
const buffer = [];
Array.isArray(items) ? items.forEach(item => this._getBoundItem(item.__boundId__, buffer)) : this._getBoundItem(items.__boundId__, buffer);
for (let element of buffer) {
releaseElement(element);
this._parentElement.removeChild(element);
}
}
_getBoundItem(id, buffer) {
const element = this._parentElement.querySelector(`[data-bound-id="${id}"]`);
if (element != undefined) {
buffer.push(element);
}
}
async _itemsAdded(items) {
if (Array.isArray(items)) {
this._addBatch(0, 100, items);
}
else {
const fragment = document.createDocumentFragment();
await this._createItemFromTemplate(items, fragment);
this._parentElement.appendChild(fragment);
}
}
async _addBatch(start, size, items) {
const fragment = document.createDocumentFragment();
const end = start + size;
for (let i = start; i <= end; i++) {
if (i > items.length - 1) {
this._parentElement.appendChild(fragment);
postProcessPropertyChange(this._parentElement, "repeat", null);
return -1;
}
const item = items[i];
await this._createItemFromTemplate(item, fragment);
}
requestAnimationFrame(() => {
this._parentElement.appendChild(fragment);
this._addBatch(end + 1, size, items);
});
}
async _createItemFromTemplate(item, fragment) {
return new Promise(async resolve => {
this._lastId += 1;
item.__boundId__ = this._lastId;
const instance = document.importNode(this._element, true);
await parseElements(instance.content.children, this, item);
Array.from(instance.content.children).forEach(child => {
child.dataset.boundId = item.__boundId__;
fragment.appendChild(child);
});
resolve();
});
}
async bind(property, attr, bindingContext) {
return new Promise(async resolve => {
PropertyBehaviour.create(attr, bindingContext, property);
resolve();
}).catch(error => console.error(error));
}
_removeAllChildren() {
const children = Array.from(this._parentElement.children).filter(element => element.nodeName != "TEMPLATE");
releaseElements(children);
for (let child of children) {
this._parentElement.removeChild(child);
}
}
dataSourceChanged(datasource) {
this._removeAllChildren();
if (datasource._data != undefined) {
this._itemsAdded(datasource._data);
}
}
}