import { Label } from '../label'; import { LayoutBase } from '../layouts/layout-base'; import { View, CSSType, CustomLayoutView, Template, KeyedTemplate } from '../core/view'; import { Property } from '../core/properties'; import { layout } from '../../utils'; import { StackLayout } from '../layouts/stack-layout'; import { ObservableArray, ChangedData } from '../../data/observable-array'; import { addWeakEventListener, removeWeakEventListener } from '../core/weak-event-listener'; import { Builder } from '../builder'; import { profile } from '../../profiling'; import { isFunction } from '../../utils/types'; export interface ItemsSource { length: number; getItem(index: number): any; } /** * Represents a UI Repeater component. */ @CSSType('Repeater') export class Repeater extends CustomLayoutView { // TODO: get rid of such hacks. public static knownFunctions = ['itemTemplateSelector']; // See component-builder.ts isKnownFunction private _isDirty = false; private _itemTemplateSelector: (item: any, index: number, items: any) => string; private _itemTemplateSelectorBindable; public ios; public android; constructor() { super(); // TODO: Do we need this as property? this.itemsLayout = new StackLayout(); } @profile public onLoaded() { if (this._isDirty) { this.refresh(); } super.onLoaded(); } /** * Gets or set the items collection of the Repeater. * The items property can be set to an array or an object defining length and getItem(index) method. */ public items: any[] | ItemsSource; /** * Gets or set the item template of the Repeater. */ public itemTemplate: string | Template; /** * Gets or set the item templates of the Repeater. */ public itemTemplates: string | Array; /** * Gets or set the items layout of the Repeater. Default value is StackLayout with orientation="vertical". */ public itemsLayout: LayoutBase; get itemTemplateSelector(): string | ((item: any, index: number, items: any) => string) { return this._itemTemplateSelector; } set itemTemplateSelector(value: string | ((item: any, index: number, items: any) => string)) { if (typeof value === 'string') { if (!this._itemTemplateSelectorBindable) { this._itemTemplateSelectorBindable = new Label(); } this._itemTemplateSelectorBindable.bind({ sourceProperty: null, targetProperty: 'templateKey', expression: value, }); this._itemTemplateSelector = (item: any, index: number, items: any) => { item['$index'] = index; if (this._itemTemplateSelectorBindable.bindingContext === item) { this._itemTemplateSelectorBindable.bindingContext = null; } this._itemTemplateSelectorBindable.bindingContext = item; return this._itemTemplateSelectorBindable.get('templateKey'); }; } else if (typeof value === 'function') { this._itemTemplateSelector = value; } } public _requestRefresh() { this._isDirty = true; if (this.isLoaded) { this.refresh(); } } /** * Forces the Repeater to reload all its items. */ public refresh() { if (this.itemsLayout) { this.itemsLayout.removeChildren(); } if (!this.items) { return; } const length = this.items.length; for (let i = 0; i < length; i++) { const dataItem = this._getDataItem(i); let viewToAdd = null; if (this._itemTemplateSelector && this.itemTemplates) { const key = this._itemTemplateSelector(dataItem, i, this.items); const length2 = this.itemTemplates.length; for (let j = 0; j < length2; j++) { const template = this.itemTemplates[j]; if (template.key === key) { viewToAdd = template.createView(); break; } } } if (!viewToAdd) { if (__UI_USE_EXTERNAL_RENDERER__) { viewToAdd = isFunction(this.itemTemplate) ? (