mirror of
https://github.com/ionic-team/ionic-framework.git
synced 2026-03-13 10:22:08 +08:00
feat(virtual-scroll): remove virtual scroll component (#25808)
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
dist
|
||||
virtual-scroll
|
||||
scripts
|
||||
proxies.ts
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"root": true,
|
||||
"ignorePatterns": ["test/**/*", "src/directives/virtual-scroll/**/*"],
|
||||
"ignorePatterns": ["test/**/*"],
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["*.ts"],
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
dist
|
||||
virtual-scroll
|
||||
scripts
|
||||
test
|
||||
src/directives/proxies.ts
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Directive, TemplateRef } from '@angular/core';
|
||||
|
||||
import { VirtualContext } from './virtual-utils';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({ selector: '[virtualFooter]' })
|
||||
export class VirtualFooter {
|
||||
constructor(public templateRef: TemplateRef<VirtualContext>) {}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Directive, TemplateRef } from '@angular/core';
|
||||
|
||||
import { VirtualContext } from './virtual-utils';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({ selector: '[virtualHeader]' })
|
||||
export class VirtualHeader {
|
||||
constructor(public templateRef: TemplateRef<VirtualContext>) {}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
import { Directive, TemplateRef, ViewContainerRef } from '@angular/core';
|
||||
|
||||
import { VirtualContext } from './virtual-utils';
|
||||
|
||||
/**
|
||||
* @hidden
|
||||
*/
|
||||
@Directive({ selector: '[virtualItem]' })
|
||||
export class VirtualItem {
|
||||
constructor(public templateRef: TemplateRef<VirtualContext>, public viewContainer: ViewContainerRef) {}
|
||||
}
|
||||
@@ -1,221 +0,0 @@
|
||||
import { ChangeDetectionStrategy, Component, ContentChild, ElementRef, EmbeddedViewRef, IterableDiffer, IterableDiffers, NgZone, SimpleChanges, TrackByFunction } from '@angular/core';
|
||||
import { Cell, CellType, FooterHeightFn, HeaderFn, HeaderHeightFn, ItemHeightFn } from '@ionic/core';
|
||||
|
||||
import { ProxyCmp } from '../angular-component-lib/utils';
|
||||
|
||||
import { VirtualFooter } from './virtual-footer';
|
||||
import { VirtualHeader } from './virtual-header';
|
||||
import { VirtualItem } from './virtual-item';
|
||||
import { VirtualContext } from './virtual-utils';
|
||||
|
||||
export declare interface IonVirtualScroll {
|
||||
/**
|
||||
* It is important to provide this
|
||||
* if virtual item height will be significantly larger than the default
|
||||
* The approximate height of each virtual item template's cell.
|
||||
* This dimension is used to help determine how many cells should
|
||||
* be created when initialized, and to help calculate the height of
|
||||
* the scrollable area. This height value can only use `px` units.
|
||||
* Note that the actual rendered size of each cell comes from the
|
||||
* app's CSS, whereas this approximation is used to help calculate
|
||||
* initial dimensions before the item has been rendered.
|
||||
*/
|
||||
approxItemHeight: number;
|
||||
|
||||
/**
|
||||
* The approximate height of each header template's cell.
|
||||
* This dimension is used to help determine how many cells should
|
||||
* be created when initialized, and to help calculate the height of
|
||||
* the scrollable area. This height value can only use `px` units.
|
||||
* Note that the actual rendered size of each cell comes from the
|
||||
* app's CSS, whereas this approximation is used to help calculate
|
||||
* initial dimensions before the item has been rendered.
|
||||
*/
|
||||
approxHeaderHeight: number;
|
||||
|
||||
/**
|
||||
* The approximate width of each footer template's cell.
|
||||
* This dimension is used to help determine how many cells should
|
||||
* be created when initialized, and to help calculate the height of
|
||||
* the scrollable area. This height value can only use `px` units.
|
||||
* Note that the actual rendered size of each cell comes from the
|
||||
* app's CSS, whereas this approximation is used to help calculate
|
||||
* initial dimensions before the item has been rendered.
|
||||
*/
|
||||
approxFooterHeight: number;
|
||||
|
||||
/**
|
||||
* Section headers and the data used within its given
|
||||
* template can be dynamically created by passing a function to `headerFn`.
|
||||
* For example, a large list of contacts usually has dividers between each
|
||||
* letter in the alphabet. App's can provide their own custom `headerFn`
|
||||
* which is called with each record within the dataset. The logic within
|
||||
* the header function can decide if the header template should be used,
|
||||
* and what data to give to the header template. The function must return
|
||||
* `null` if a header cell shouldn't be created.
|
||||
*/
|
||||
headerFn?: HeaderFn;
|
||||
|
||||
/**
|
||||
* Section footers and the data used within its given
|
||||
* template can be dynamically created by passing a function to `footerFn`.
|
||||
* The logic within the footer function can decide if the footer template
|
||||
* should be used, and what data to give to the footer template. The function
|
||||
* must return `null` if a footer cell shouldn't be created.
|
||||
*/
|
||||
footerFn?: HeaderFn;
|
||||
|
||||
/**
|
||||
* The data that builds the templates within the virtual scroll.
|
||||
* It's important to note that when this data has changed, then the
|
||||
* entire virtual scroll is reset, which is an expensive operation and
|
||||
* should be avoided if possible.
|
||||
*/
|
||||
items?: any[] | null;
|
||||
|
||||
/**
|
||||
* An optional function that maps each item within their height.
|
||||
* When this function is provided, heavy optimizations and fast path can be taked by
|
||||
* `ion-virtual-scroll` leading to massive performance improvements.
|
||||
*
|
||||
* This function allows to skip all DOM reads, which can be Doing so leads
|
||||
* to massive performance
|
||||
*/
|
||||
itemHeight?: ItemHeightFn;
|
||||
|
||||
/**
|
||||
* An optional function that maps each item header within their height.
|
||||
*/
|
||||
headerHeight?: HeaderHeightFn;
|
||||
|
||||
/**
|
||||
* An optional function that maps each item footer within their height.
|
||||
*/
|
||||
footerHeight?: FooterHeightFn;
|
||||
|
||||
/**
|
||||
* Same as `ngForTrackBy` which can be used on `ngFor`.
|
||||
*/
|
||||
trackBy: TrackByFunction<any>;
|
||||
|
||||
/**
|
||||
* This method marks the tail the items array as dirty, so they can be re-rendered. It's equivalent to calling: ```js * virtualScroll.checkRange(lastItemLen, items.length - lastItemLen); * ```
|
||||
*/
|
||||
'checkEnd': () => void;
|
||||
/**
|
||||
* This method marks a subset of items as dirty, so they can be re-rendered. Items should be marked as dirty any time the content or their style changes. The subset of items to be updated can are specifing by an offset and a length.
|
||||
*/
|
||||
'checkRange': (offset: number, len?: number) => void;
|
||||
/**
|
||||
* Returns the position of the virtual item at the given index.
|
||||
*/
|
||||
'positionForItem': (index: number) => Promise<number>;
|
||||
}
|
||||
|
||||
@ProxyCmp({
|
||||
inputs: ['approxItemHeight', 'approxHeaderHeight', 'approxFooterHeight', 'headerFn', 'footerFn', 'items', 'itemHeight', 'headerHeight', 'footerHeight'],
|
||||
methods: ['checkEnd', 'checkRange', 'positionForItem']
|
||||
})
|
||||
@Component({
|
||||
selector: 'ion-virtual-scroll',
|
||||
template: '<ng-content></ng-content>',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush,
|
||||
inputs: [
|
||||
'approxItemHeight',
|
||||
'approxHeaderHeight',
|
||||
'approxFooterHeight',
|
||||
'headerFn',
|
||||
'footerFn',
|
||||
'items',
|
||||
'itemHeight',
|
||||
'headerHeight',
|
||||
'footerHeight',
|
||||
'trackBy'
|
||||
]
|
||||
})
|
||||
export class IonVirtualScroll {
|
||||
|
||||
private differ?: IterableDiffer<any>;
|
||||
private el: HTMLIonVirtualScrollElement;
|
||||
private refMap = new WeakMap<HTMLElement, EmbeddedViewRef<VirtualContext>>();
|
||||
|
||||
@ContentChild(VirtualItem, { static: false }) itmTmp!: VirtualItem;
|
||||
@ContentChild(VirtualHeader, { static: false }) hdrTmp!: VirtualHeader;
|
||||
@ContentChild(VirtualFooter, { static: false }) ftrTmp!: VirtualFooter;
|
||||
|
||||
constructor(
|
||||
private z: NgZone,
|
||||
private iterableDiffers: IterableDiffers,
|
||||
elementRef: ElementRef,
|
||||
) {
|
||||
this.el = elementRef.nativeElement as HTMLIonVirtualScrollElement;
|
||||
this.el.nodeRender = this.nodeRender.bind(this);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (this.trackBy && 'items' in changes) {
|
||||
// React on virtualScroll changes only once all inputs have been initialized
|
||||
const value = changes['items'].currentValue;
|
||||
if (this.differ === undefined && value != null) {
|
||||
try {
|
||||
this.differ = this.iterableDiffers.find(value).create(this.trackBy);
|
||||
} catch (e) {
|
||||
throw new Error(
|
||||
`Cannot find a differ supporting object '${value}'. VirtualScroll only supports binding to Iterables such as Arrays.`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ngDoCheck() {
|
||||
// and if there actually are changes
|
||||
const changes = this.differ !== undefined && this.items ? this.differ.diff(this.items) : null;
|
||||
if (changes === null) {
|
||||
return;
|
||||
}
|
||||
// TODO: optimize
|
||||
this.checkRange(0);
|
||||
}
|
||||
|
||||
private nodeRender(el: HTMLElement | null, cell: Cell, index: number): HTMLElement {
|
||||
return this.z.run(() => {
|
||||
let node: EmbeddedViewRef<VirtualContext>;
|
||||
if (!el) {
|
||||
node = this.itmTmp.viewContainer.createEmbeddedView(
|
||||
this.getComponent(cell.type),
|
||||
{ $implicit: cell.value, index },
|
||||
index
|
||||
);
|
||||
el = getElement(node);
|
||||
this.refMap.set(el, node);
|
||||
} else {
|
||||
node = this.refMap.get(el)!;
|
||||
const ctx = node.context;
|
||||
ctx.$implicit = cell.value;
|
||||
ctx.index = cell.index;
|
||||
}
|
||||
// run sync change detections
|
||||
node.detectChanges();
|
||||
return el;
|
||||
});
|
||||
}
|
||||
|
||||
private getComponent(type: CellType) {
|
||||
switch (type) {
|
||||
case 'item': return this.itmTmp.templateRef;
|
||||
case 'header': return this.hdrTmp.templateRef;
|
||||
case 'footer': return this.ftrTmp.templateRef;
|
||||
default: throw new Error('template for virtual item was not provided');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const getElement = (view: EmbeddedViewRef<VirtualContext>): HTMLElement => {
|
||||
const rootNodes = view.rootNodes;
|
||||
for (let i = 0; i < rootNodes.length; i++) {
|
||||
if (rootNodes[i].nodeType === 1) {
|
||||
return rootNodes[i];
|
||||
}
|
||||
}
|
||||
throw new Error('virtual element was not created');
|
||||
};
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
export interface VirtualContext {
|
||||
$implicit: any;
|
||||
index: number;
|
||||
}
|
||||
@@ -14,10 +14,6 @@ export {
|
||||
} from './directives/navigation/router-link-delegate';
|
||||
|
||||
export { NavParams } from './directives/navigation/nav-params';
|
||||
export { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
|
||||
export { VirtualItem } from './directives/virtual-scroll/virtual-item';
|
||||
export { VirtualHeader } from './directives/virtual-scroll/virtual-header';
|
||||
export { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
|
||||
export { IonModal } from './directives/overlays/modal';
|
||||
export { IonPopover } from './directives/overlays/popover';
|
||||
export * from './directives/proxies';
|
||||
|
||||
@@ -19,10 +19,6 @@ import {
|
||||
import { IonModal } from './directives/overlays/modal';
|
||||
import { IonPopover } from './directives/overlays/popover';
|
||||
import { DIRECTIVES } from './directives/proxies-list';
|
||||
import { VirtualFooter } from './directives/virtual-scroll/virtual-footer';
|
||||
import { VirtualHeader } from './directives/virtual-scroll/virtual-header';
|
||||
import { VirtualItem } from './directives/virtual-scroll/virtual-item';
|
||||
import { IonVirtualScroll } from './directives/virtual-scroll/virtual-scroll';
|
||||
import { AngularDelegate } from './providers/angular-delegate';
|
||||
import { ConfigToken } from './providers/config';
|
||||
import { ModalController } from './providers/modal-controller';
|
||||
@@ -50,12 +46,6 @@ const DECLARATIONS = [
|
||||
NavDelegate,
|
||||
RouterLinkDelegateDirective,
|
||||
RouterLinkWithHrefDelegateDirective,
|
||||
|
||||
// virtual scroll
|
||||
VirtualFooter,
|
||||
VirtualHeader,
|
||||
VirtualItem,
|
||||
IonVirtualScroll,
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
describe('Virtual Scroll', () => {
|
||||
beforeEach(() => {
|
||||
cy.visit('/virtual-scroll');
|
||||
cy.wait(30);
|
||||
})
|
||||
|
||||
it('should open virtual-scroll', () => {
|
||||
cy.get('ion-virtual-scroll > *').its('length').should('be.gt', 0);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -7,8 +7,6 @@ import { RouterLinkPageComponent } from './router-link-page/router-link-page.com
|
||||
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
|
||||
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
|
||||
import { HomePageComponent } from './home-page/home-page.component';
|
||||
import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component';
|
||||
import { VirtualScrollDetailComponent } from './virtual-scroll-detail/virtual-scroll-detail.component';
|
||||
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
|
||||
import { NestedOutletPageComponent } from './nested-outlet-page/nested-outlet-page.component';
|
||||
import { NestedOutletPage2Component } from './nested-outlet-page2/nested-outlet-page2.component';
|
||||
@@ -40,8 +38,6 @@ const routes: Routes = [
|
||||
{ path: 'router-link-page2/:id', component: RouterLinkPage2Component },
|
||||
{ path: 'router-link-page3', component: RouterLinkPage3Component },
|
||||
{ path: 'slides', component: SlidesComponent },
|
||||
{ path: 'virtual-scroll', component: VirtualScrollComponent },
|
||||
{ path: 'virtual-scroll-detail/:itemId', component: VirtualScrollDetailComponent },
|
||||
{ path: 'tabs', redirectTo: '/tabs/account', pathMatch: 'full' },
|
||||
{
|
||||
path: 'navigation',
|
||||
|
||||
@@ -15,9 +15,6 @@ import { RouterLinkPageComponent } from './router-link-page/router-link-page.com
|
||||
import { RouterLinkPage2Component } from './router-link-page2/router-link-page2.component';
|
||||
import { RouterLinkPage3Component } from './router-link-page3/router-link-page3.component';
|
||||
import { HomePageComponent } from './home-page/home-page.component';
|
||||
import { VirtualScrollComponent } from './virtual-scroll/virtual-scroll.component';
|
||||
import { VirtualScrollDetailComponent } from './virtual-scroll-detail/virtual-scroll-detail.component';
|
||||
import { VirtualScrollInnerComponent } from './virtual-scroll-inner/virtual-scroll-inner.component';
|
||||
import { NestedOutletComponent } from './nested-outlet/nested-outlet.component';
|
||||
import { NestedOutletPageComponent } from './nested-outlet-page/nested-outlet-page.component';
|
||||
import { NestedOutletPage2Component } from './nested-outlet-page2/nested-outlet-page2.component';
|
||||
@@ -44,9 +41,6 @@ import { AccordionModalComponent } from './accordion/accordion-modal/accordion-m
|
||||
RouterLinkPage2Component,
|
||||
RouterLinkPage3Component,
|
||||
HomePageComponent,
|
||||
VirtualScrollComponent,
|
||||
VirtualScrollDetailComponent,
|
||||
VirtualScrollInnerComponent,
|
||||
NestedOutletComponent,
|
||||
NestedOutletPageComponent,
|
||||
NestedOutletPage2Component,
|
||||
|
||||
@@ -1,71 +1,66 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Test App
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item routerLink="/alerts" [routerAnimation]="routerAnimation">
|
||||
<ion-label>
|
||||
Alerts test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/inputs">
|
||||
<ion-label>
|
||||
Inputs test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/form">
|
||||
<ion-label>
|
||||
Form test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/modals">
|
||||
<ion-label>
|
||||
Modals test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/router-link">
|
||||
<ion-label>
|
||||
Router link test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/tabs">
|
||||
<ion-label>
|
||||
Tabs test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/slides">
|
||||
<ion-label>
|
||||
Slides test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/virtual-scroll">
|
||||
<ion-label>
|
||||
Virtual Scroll
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/nested-outlet/page">
|
||||
<ion-label>
|
||||
Nested ion-router-outlet
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/view-child">
|
||||
<ion-label>
|
||||
ViewChild()
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/providers">
|
||||
<ion-label>
|
||||
Providers
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/accordions">
|
||||
<ion-label>
|
||||
Accordions Test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Test App
|
||||
</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-list>
|
||||
<ion-item routerLink="/alerts" [routerAnimation]="routerAnimation">
|
||||
<ion-label>
|
||||
Alerts test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/inputs">
|
||||
<ion-label>
|
||||
Inputs test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/form">
|
||||
<ion-label>
|
||||
Form test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/modals">
|
||||
<ion-label>
|
||||
Modals test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/router-link">
|
||||
<ion-label>
|
||||
Router link test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/tabs">
|
||||
<ion-label>
|
||||
Tabs test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/slides">
|
||||
<ion-label>
|
||||
Slides test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/nested-outlet/page">
|
||||
<ion-label>
|
||||
Nested ion-router-outlet
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/view-child">
|
||||
<ion-label>
|
||||
ViewChild()
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/providers">
|
||||
<ion-label>
|
||||
Providers
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
<ion-item routerLink="/accordions">
|
||||
<ion-label>
|
||||
Accordions Test
|
||||
</ion-label>
|
||||
</ion-item>
|
||||
</ion-list>
|
||||
</ion-content>
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-buttons slot="start">
|
||||
<ion-back-button></ion-back-button>
|
||||
</ion-buttons>
|
||||
<ion-title>virtual-scroll page</ion-title>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
|
||||
<ion-content class="ion-padding">
|
||||
<h1>Item {{itemNu}}</h1>
|
||||
<p>ngOnInit: <span id="ngOnInit">{{onInit}}</span></p>
|
||||
<p>ionViewWillEnter: <span id="ionViewWillEnter">{{willEnter}}</span></p>
|
||||
<p>ionViewDidEnter: <span id="ionViewDidEnter">{{didEnter}}</span></p>
|
||||
<p>ionViewWillLeave: <span id="ionViewWillLeave">{{willLeave}}</span></p>
|
||||
<p>ionViewDidLeave: <span id="ionViewDidLeave">{{didLeave}}</span></p>
|
||||
</ion-content>
|
||||
@@ -1,46 +0,0 @@
|
||||
import { Component, NgZone, OnInit } from '@angular/core';
|
||||
import { ActivatedRoute } from '@angular/router';
|
||||
import { ViewDidEnter, ViewDidLeave, ViewWillEnter, ViewWillLeave } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-virtual-scroll-detail',
|
||||
templateUrl: './virtual-scroll-detail.component.html',
|
||||
})
|
||||
export class VirtualScrollDetailComponent implements OnInit, ViewWillEnter, ViewDidEnter, ViewWillLeave, ViewDidLeave {
|
||||
|
||||
onInit = 0;
|
||||
willEnter = 0;
|
||||
didEnter = 0;
|
||||
willLeave = 0;
|
||||
didLeave = 0;
|
||||
|
||||
itemNu = 'none';
|
||||
|
||||
constructor(private route: ActivatedRoute) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.itemNu = this.route.snapshot.paramMap.get('itemId');
|
||||
NgZone.assertInAngularZone();
|
||||
this.onInit++;
|
||||
}
|
||||
|
||||
ionViewWillEnter() {
|
||||
if (this.onInit !== 1) {
|
||||
throw new Error('ngOnInit was not called');
|
||||
}
|
||||
NgZone.assertInAngularZone();
|
||||
this.willEnter++;
|
||||
}
|
||||
ionViewDidEnter() {
|
||||
NgZone.assertInAngularZone();
|
||||
this.didEnter++;
|
||||
}
|
||||
ionViewWillLeave() {
|
||||
NgZone.assertInAngularZone();
|
||||
this.willLeave++;
|
||||
}
|
||||
ionViewDidLeave() {
|
||||
NgZone.assertInAngularZone();
|
||||
this.didLeave++;
|
||||
}
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
<p>
|
||||
[{{onInit}}] Item {{value}}
|
||||
</p>
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Component, OnInit, NgZone, Input } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-virtual-scroll-inner',
|
||||
templateUrl: './virtual-scroll-inner.component.html',
|
||||
})
|
||||
export class VirtualScrollInnerComponent implements OnInit {
|
||||
|
||||
@Input() value: string;
|
||||
onInit = 0;
|
||||
|
||||
ngOnInit() {
|
||||
NgZone.assertInAngularZone();
|
||||
this.onInit++;
|
||||
console.log('created');
|
||||
}
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
<ion-header>
|
||||
<ion-toolbar>
|
||||
<ion-title>
|
||||
Virtual Scroll Test
|
||||
</ion-title>
|
||||
<ion-buttons slot="end">
|
||||
<ion-button (click)="addItems()">
|
||||
<ion-icon name="add" slot="icon-only"></ion-icon>
|
||||
</ion-button>
|
||||
</ion-buttons>
|
||||
</ion-toolbar>
|
||||
</ion-header>
|
||||
<ion-content>
|
||||
<ion-virtual-scroll [items]="items" [headerFn]="myHeaderFn" [footerFn]="myFooterFn">
|
||||
<ion-item-divider *virtualHeader="let header">{{ header }}</ion-item-divider>
|
||||
<ion-item-divider *virtualFooter="let footer">-- {{ footer }}</ion-item-divider>
|
||||
<!-- <ion-item *virtualItem="let item" itemHeight="itemHeight">
|
||||
{{item.name}}
|
||||
</ion-item> -->
|
||||
<ion-item *virtualItem="let item" [routerLink]="['/', 'virtual-scroll-detail', item]">
|
||||
<ion-label>
|
||||
<app-virtual-scroll-inner [value]="item.name"></app-virtual-scroll-inner>
|
||||
</ion-label>
|
||||
<ion-icon *ngIf="(item.name % 2) === 0" name="airplane" slot="start"></ion-icon>
|
||||
<ion-toggle slot="end" [(ngModel)]="item.checked"></ion-toggle>
|
||||
</ion-item>
|
||||
</ion-virtual-scroll>
|
||||
</ion-content>
|
||||
@@ -1,36 +0,0 @@
|
||||
import { Component, OnInit, ViewChild } from '@angular/core';
|
||||
import { HeaderFn } from '@ionic/core';
|
||||
import { IonVirtualScroll } from '@ionic/angular';
|
||||
|
||||
@Component({
|
||||
selector: 'app-virtual-scroll',
|
||||
templateUrl: './virtual-scroll.component.html',
|
||||
})
|
||||
export class VirtualScrollComponent {
|
||||
|
||||
@ViewChild(IonVirtualScroll, { static: true }) virtualScroll: IonVirtualScroll;
|
||||
|
||||
items = Array.from({length: 100}, (_, i) => ({ name: `${i}`, checked: true}));
|
||||
|
||||
itemHeight = () => 44;
|
||||
|
||||
myHeaderFn: HeaderFn = (_, index) => {
|
||||
if ((index % 10) === 0) {
|
||||
return `Header ${index}`;
|
||||
}
|
||||
}
|
||||
|
||||
myFooterFn: HeaderFn = (_, index) => {
|
||||
if ((index % 5) === 0) {
|
||||
return `Footer ${index}`;
|
||||
}
|
||||
}
|
||||
|
||||
addItems() {
|
||||
console.log('adding items');
|
||||
this.items.push(
|
||||
{ name: `New Item`, checked: true}
|
||||
);
|
||||
this.virtualScroll.checkEnd();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user