UNPKG

pragma-views2

Version:

188 lines (154 loc) 6.69 kB
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); } } }