feat(virtual-scroller): add <template> support

This commit is contained in:
Manu Mtz.-Almeida
2018-09-02 18:30:19 +02:00
parent 281f9a307f
commit d40d0a706f
3 changed files with 61 additions and 65 deletions

View File

@ -30,67 +30,36 @@
<p> <p>
<ion-button onclick="addItems()">Add Items</ion-button> <ion-button onclick="addItems()">Add Items</ion-button>
</p> </p>
<ion-virtual-scroll id="virtual"></ion-virtual-scroll>
<ion-virtual-scroll id="virtual">
<template>
<ion-item>
<ion-label></ion-label>
</ion-item>
</template>
</ion-virtual-scroll>
<ion-infinite-scroll threshold="100px" id="infinite-scroll"> <ion-infinite-scroll threshold="100px" id="infinite-scroll">
<ion-infinite-scroll-content loadingSpinner="bubbles" loadingText="Loading more data..."> <ion-infinite-scroll-content loadingSpinner="bubbles" loadingText="Loading more data...">
</ion-infinite-scroll-content> </ion-infinite-scroll-content>
</ion-infinite-scroll> </ion-infinite-scroll>
</ion-content> </ion-content>
</ion-app> </ion-app>
<script> <script>
const virtual = document.getElementById('virtual');
const items = Array.from({ length: 100 }, (x, i) => i); const items = Array.from({ length: 100 }, (x, i) => i);
const virtual = document.getElementById('virtual');
function addItems(append) { virtual.itemHeight = () => 44;
if (!append) {
append = Array.from({ length: 10 }, (x, i) => "append" + i);
}
items.push(...append);
virtual.markDirtyTail(append.length)
}
virtual.itemHeight = () => 45;
virtual.headerFn = (item, index) => {
// if (index % 20 === 0) {
// return 'Header ' + index;
// }
return null;
}
function renderItem(el, item) {
if (!el) {
el = document.createElement('ion-item');
const text = document.createTextNode(item);
el['$content'] = text;
el.appendChild(text);
} else {
el['$content'].nodeValue = item;
}
return el;
}
function renderHeader(el, item) {
if (!el) {
el = document.createElement('ion-item-divider');
const text = document.createTextNode(item);
el['$content'] = text;
el.appendChild(text);
} else {
el['$content'].nodeValue = item;
}
return el;
}
virtual.nodeRender = (el, cell) => {
if (cell.type === 0) return renderItem(el, cell.value);
return renderHeader(el, cell.value);
};
virtual.items = items; virtual.items = items;
virtual.nodeRender = (el, cell) => {
if (cell.type === 0) {
renderItem(el, cell.value);
}
};
function renderItem(el, item) {
el.querySelector('ion-label').textContent = item;
}
const infiniteScroll = document.getElementById('infinite-scroll'); const infiniteScroll = document.getElementById('infinite-scroll');
@ -103,6 +72,14 @@
console.log('Done'); console.log('Done');
}); });
function addItems(append) {
if (!append) {
append = Array.from({ length: 10 }, (x, i) => "append" + i);
}
items.push(...append);
virtual.markDirtyTail(append.length)
}
function getAsyncData() { function getAsyncData() {
return new Promise(resolve => { return new Promise(resolve => {
setTimeout(() => { setTimeout(() => {

View File

@ -20,11 +20,20 @@
</ion-header> </ion-header>
<ion-content> <ion-content>
<ion-virtual-scroll id="virtual"></ion-virtual-scroll>
<ion-virtual-scroll id="virtual">
<template>
<ion-card>
<ion-card-header>
<ion-card-title>Card Header</ion-card-title>
</ion-card-header>
<ion-card-content class="content"></ion-card-content>
</ion-card>
</template>
</ion-virtual-scroll>
</ion-content> </ion-content>
</ion-app> </ion-app>
<script> <script>
@ -38,14 +47,7 @@
} }
function renderItem(el, item) { function renderItem(el, item) {
if (!el) { el.querySelector('.content').textContent = item.content;
el = document.createElement('ion-card');
}
el.innerHTML = `
<ion-card-header>
<ion-card-title>Card Header</ion-card-title>
</ion-card-header>
<ion-card-content>${item.content}</ion-card-content>`;
return el; return el;
} }

View File

@ -75,7 +75,7 @@ export function doRender(
dom: VirtualNode[], dom: VirtualNode[],
updateCellHeight: (cell: Cell, node: HTMLElement) => void updateCellHeight: (cell: Cell, node: HTMLElement) => void
) { ) {
const children = el.children; const children = Array.from(el.children).filter(n => n.tagName !== 'TEMPLATE');
const childrenNu = children.length; const childrenNu = children.length;
let child: HTMLElement; let child: HTMLElement;
for (let i = 0; i < dom.length; i++) { for (let i = 0; i < dom.length; i++) {
@ -88,9 +88,10 @@ export function doRender(
child = children[i] as HTMLElement; child = children[i] as HTMLElement;
nodeRender(child, cell, i); nodeRender(child, cell, i);
} else { } else {
child = nodeRender(null, cell, i); const newChild = createNode(el, cell.type);
child = nodeRender(newChild, cell, i) || newChild;
child.classList.add('virtual-item'); child.classList.add('virtual-item');
el.appendChild(child); el.appendChild(child!);
} }
(child as any)['$ionCell'] = cell; (child as any)['$ionCell'] = cell;
} else { } else {
@ -121,6 +122,22 @@ export function doRender(
} }
} }
function createNode(el: HTMLElement, type: CellType): HTMLElement | null {
const template = getTemplate(el, type);
if (template) {
return el.ownerDocument.importNode(template.content, true).children[0] as HTMLElement;
}
return null;
}
function getTemplate(el: HTMLElement, type: CellType): HTMLTemplateElement | null {
switch (type) {
case CellType.Item: return el.querySelector('template:not([name])');
case CellType.Header: return el.querySelector('template[name=header]');
case CellType.Footer: return el.querySelector('template[name=footer]');
}
}
export function getViewport(scrollTop: number, vierportHeight: number, margin: number): Viewport { export function getViewport(scrollTop: number, vierportHeight: number, margin: number): Viewport {
return { return {
top: Math.max(scrollTop - margin, 0), top: Math.max(scrollTop - margin, 0),