• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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> ![icon-note.gif](public_sys-resources/icon-note.gif) **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