1# LazyForEach 2 3 4The development framework provides LazyForEach to iterate data from provided data sources and create corresponding components during each iteration. LazyForEach is defined as follows: 5 6 7 8``` 9LazyForEach( 10 dataSource: IDataSource, // Data source to be iterated 11 itemGenerator: (item: any) => void, // child component generator 12 keyGenerator?: (item: any) => string // (optional) Unique key generator, which is recommended. 13): void 14 15interface IDataSource { 16 totalCount(): number; // Get total count of data 17 getData(index: number): any; // Get single data by index 18 registerDataChangeListener(listener: DataChangeListener): void; // Register listener to listening data changes 19 unregisterDataChangeListener(listener: DataChangeListener): void; // Unregister listener 20} 21 22interface DataChangeListener { 23 onDataReloaded(): void; // Called while data reloaded 24 onDataAdded(index: number): void; // Called while single data added 25 onDataMoved(from: number, to: number): void; // Called while single data moved 26 onDataDeleted(index: number): void; // Called while single data deleted 27 onDataChanged(index: number): void; // Called while single data changed 28} 29``` 30 31 32## APIs 33 34 35### LazyForEach 36 37LazyForEach(dataSource: IDataSource, itemGenerator: (item: any) => void, keyGenerator?: (item: any) => string):void 38 39 Table1 Parameters 40 41| Name | Type | Mandatory | Default Value | Description | 42| -------- | -------- | -------- | -------- | -------- | 43| dataSource | IDataSource | Yes | - | Object used to implement the IDataSource API. You need to implement related APIs. | 44| itemGenerator | (item: any) => void | Yes | - | Used to generate the lambda function of the child components. It generates one or more child components for a given array item. A single component and its child component list must be contained in the braces ({...}) | 45| keyGenerator | (item: any) => string | No | - | Used as an anonymous parameter for generating a unique and stable key value for a given array item. When the position of a subitem in the array is changed, the key value of the subitem cannot be changed. When a subitem in the array is replaced with a new item, the key value of the current item must be different from that of the new item. This key-value generator is optional. However, for performance reasons, it is strongly recommended that the key-value generator be provided, so that the development framework can better identify array changes. If the array is reversed while no key-value generator is provided, all nodes in LazyForEach will be rebuilt. | 46 47 48 Table2 Description of IDataSource 49 50| Name | Description | 51| -------- | -------- | 52| totalCount(): number | Obtains the total number of data records. | 53| getData(index: number): any | Obtains the data corresponding to the specified index. | 54| registerDataChangeListener(listener: DataChangeListener): void | Registers the data change listener. | 55| unregisterDataChangeListener(listener: DataChangeListener): void | Unregisters the data change listener. | 56 57 58 Table3 Description of DataChangeListener 59 60| Name | Description | 61| -------- | -------- | 62| onDataReloaded(): void | Reloads all data. | 63| onDataAdded(index: number): void | Notifies the component that data is added to the position indicated by the specified index. | 64| onDataMoved(from: number, to: number): void | Notifies the component that data is moved from the from position to the to position. | 65| onDataDeleted(index: number): void | Notifies the component that data is deleted from the position indicated by the specified index. | 66| onDataChanged(index: number): void | Notifies the component that data in the position indicated by the specified index is changed. | 67 68 69>  **NOTE**: 70> - LazyForEach must be used in the container component. Only the <List>, <Grid>, and <Swiper> components support LazyForEach (that is, only the visible part and a small amount of data before and after the visible part are loaded for caching). For other components, all data is loaded at a time. 71> 72> - LazyForEach must create one and only one child component in each iteration. 73> 74> - The generated child component must be in the parent container component of LazyForEach. 75> 76> - LazyForEach can be included in an if/else conditional statement, but cannot contain an if/else conditional statement. 77> 78> - For the purpose of high-performance rendering, when the onDataChanged method of the DataChangeListener object is used to update the UI, the component update is triggered only when the state variable is used in the component specified in the UI description of itemGenerator. 79> 80> - The calling sequence of the subitem generator function may be different from that of the data items in the data source. During the development, do not assume whether the subitem generator and key value generator functions are executed and the execution sequence. The following is an example of incorrect usage: 81> 82> ``` 83> LazyForEach(dataSource, item => {Text(`${++counter}. item.label`)}) 84> ``` 85> 86> Below is an example of correct usage: 87> 88> 89> ``` 90> LazyForEach(dataSource, 91> item => Text(`${item.i}. item.data.label`)), 92> item => item.data.id.toString()) 93> ``` 94 95 96## Example 97 98 99``` 100// Basic implementation of IDataSource to handle data listener 101class BasicDataSource implements IDataSource { 102 private listeners: DataChangeListener[] = [] 103 104 public totalCount(): number { 105 return 0 106 } 107 public getData(index: number): any { 108 return undefined 109 } 110 111 registerDataChangeListener(listener: DataChangeListener): void { 112 if (this.listeners.indexOf(listener) < 0) { 113 console.info('add listener') 114 this.listeners.push(listener) 115 } 116 } 117 unregisterDataChangeListener(listener: DataChangeListener): void { 118 const pos = this.listeners.indexOf(listener); 119 if (pos >= 0) { 120 console.info('remove listener') 121 this.listeners.splice(pos, 1) 122 } 123 } 124 125 notifyDataReload(): void { 126 this.listeners.forEach(listener => { 127 listener.onDataReloaded() 128 }) 129 } 130 notifyDataAdd(index: number): void { 131 this.listeners.forEach(listener => { 132 listener.onDataAdded(index) 133 }) 134 } 135 notifyDataChange(index: number): void { 136 this.listeners.forEach(listener => { 137 listener.onDataChanged(index) 138 }) 139 } 140 notifyDataDelete(index: number): void { 141 this.listeners.forEach(listener => { 142 listener.onDataDeleted(index) 143 }) 144 } 145 notifyDataMove(from: number, to: number): void { 146 this.listeners.forEach(listener => { 147 listener.onDataMoved(from, to) 148 }) 149 } 150} 151 152class MyDataSource extends BasicDataSource { 153 private dataArray: string[] = ['/path/image0', '/path/image1', '/path/image2', '/path/image3'] 154 155 public totalCount(): number { 156 return this.dataArray.length 157 } 158 public getData(index: number): any { 159 return this.dataArray[index] 160 } 161 162 public addData(index: number, data: string): void { 163 this.dataArray.splice(index, 0, data) 164 this.notifyDataAdd(index) 165 } 166 public pushData(data: string): void { 167 this.dataArray.push(data) 168 this.notifyDataAdd(this.dataArray.length - 1) 169 } 170} 171 172@Entry 173@Component 174struct MyComponent { 175 private data: MyDataSource = new MyDataSource() 176 build() { 177 List({space: 3}) { 178 LazyForEach(this.data, (item: string) => { 179 ListItem() { 180 Row() { 181 Image(item).width("30%").height(50) 182 Text(item).fontSize(20).margin({left:10}) 183 }.margin({left: 10, right: 10}) 184 } 185 .onClick(()=>{ 186 this.data.pushData('/path/image' + this.data.totalCount()) 187 }) 188 }, item => item) 189 } 190 } 191} 192``` 193