• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@Builder Decorator: Custom Builder Function
2
3ArkUI provides the \@Builder decorator that is a lightweight UI element reuse mechanism. This custom component has a fixed internal UI structure and passes the data only to the user. You can abstract reused UI elements into a method and call the method in the **build** method.
4
5For simplicity, here we refer to an \@Builder decorated function also as a custom builder function.
6
7
8> **NOTE**
9>
10> This decorator can be used in ArkTS widgets since API version 9.
11>
12> This decorator can be used in atomic services since API version 11.
13
14## Constraints
15
16- The \@Builder triggers dynamic UI rendering for only when parameters are passed in by reference. Only one parameter can be passed.
17
18- If the \@Builder passes in two or more parameters, dynamic UI rendering is not triggered.
19
20- If the \@Builder passes in parameters by value and by reference, dynamic UI rendering is not triggered.
21
22- \@Builder parameters must be passed in one by one in the form of object literals to trigger dynamic UI rendering.
23
24## Rules of Use
25
26### Private Custom Builder Function
27
28Syntax:
29
30```ts
31@Builder MyBuilderFunction() {}
32```
33
34Usage:
35
36```ts
37this.MyBuilderFunction()
38```
39
40- You can define one or more @Builder decorated methods in a custom component. Such a method is considered as a private, special type of member function of the component.
41
42- The custom builder function can be called from the **build** method or another custom builder function in the same component only.
43
44- Inside the custom builder function body, **this** refers to the owning component. Component state variables are accessible from within the custom builder function implementation. Using **this** to access the custom components' state variables is recommended over parameter passing.
45
46
47### Global Custom Builder Function
48
49Syntax:
50
51```ts
52@Builder function MyGlobalBuilderFunction() { ... }
53```
54
55Usage:
56
57```ts
58MyGlobalBuilderFunction()
59```
60
61- Use of a global custom builder function is recommended if no own state is involved.
62
63
64## Parameter Passing Rules
65
66For custom builder functions, parameters can be passed [by value](#by-value-parameter-passing) and [by reference](#by-reference-parameter-passing). Both of them must comply with the following rules:
67
68- The parameter type must be the same as the declared parameter type. The **undefined** or **null** constants as well as expressions evaluating to these values are not allowed.
69
70- All parameters must be immutable inside the custom builder function.
71
72- The custom builder function body follows the same [syntax rules](arkts-create-custom-components.md#build-function) as the **build()** function.
73
74- Parameters are passed by value in all cases except when only one parameter is passed in and the parameter needs to be directly passed to the object literal.
75
76
77### By-Reference Parameter Passing
78
79In by-reference parameter passing, state variables can be passed, and the change of these state variables causes the UI re-rendering in the \@Builder decorated method.
80
81```ts
82class Tmp {
83  paramA1: string = ''
84}
85
86@Builder function overBuilder(params: Tmp) {
87  Row() {
88    Text(`UseStateVarByReference: ${params.paramA1} `)
89  }
90}
91@Entry
92@Component
93struct Parent {
94  @State label: string = 'Hello';
95  build() {
96    Column() {
97      // Pass the this.label reference to the overBuilder component when the overBuilder component is called in the Parent component.
98      overBuilder({ paramA1: this.label })
99      Button('Click me').onClick(() => {
100        // After Click me is clicked, the UI text changes from Hello to ArkUI.
101        this.label = 'ArkUI';
102      })
103    }
104  }
105}
106```
107
108When parameters are passed by reference, if a custom component is called within the \@Builder method, ArkUI provides [$$](arkts-two-way-sync.md) as the paradigm for passing parameters by reference.
109
110```ts
111class Tmp {
112  paramA1: string = ''
113}
114
115@Builder function overBuilder($$: Tmp) {
116  Row() {
117    Column() {
118      Text(`overBuilder===${$$.paramA1}`)
119      HelloComponent({message: $$.paramA1})
120    }
121  }
122}
123
124@Component
125struct HelloComponent {
126  @Prop message: string;
127
128  build() {
129    Row() {
130      Text(`HelloComponent===${this.message}`)
131    }
132  }
133}
134
135@Entry
136@Component
137struct Parent {
138  @State label: string = 'Hello';
139  build() {
140    Column() {
141      // Pass the this.label reference to the overBuilder component when the overBuilder component is called in the Parent component.
142      overBuilder({paramA1: this.label})
143      Button('Click me').onClick(() => {
144        // After Click me is clicked, the UI text changes from Hello to ArkUI.
145        this.label = 'ArkUI';
146      })
147    }
148  }
149}
150```
151
152### By-Value Parameter Passing
153
154By default, parameters in the \@Builder decorated functions are passed by value. In this case, when the passed parameter is a state variable, the change of the state variable does not cause UI re-rendering in the \@Builder decorated function. Therefore, when passing state variables, you are advised to use [by-reference parameter passing](#by-reference-parameter-passing).
155
156
157```ts
158@Builder function overBuilder(paramA1: string) {
159  Row() {
160    Text(`UseStateVarByValue: ${paramA1} `)
161  }
162}
163@Entry
164@Component
165struct Parent {
166  @State label: string = 'Hello';
167  build() {
168    Column() {
169      overBuilder(this.label)
170    }
171  }
172}
173```
174
175## Use Scenarios
176
177### Using Custom Builder Function in Custom Component
178
179Create a private \@Builder method, call this method by using **this.builder()** in **Column**, and change the content of **builder_value** through the **aboutToAppear** lifecycle function and Button click event to dynamically render the UI.
180
181```ts
182@Entry
183@Component
184struct PrivateBuilder {
185  @State builder_value: string = 'Hello';
186
187  @Builder builder() {
188    Column(){
189      Text(this.builder_value)
190        .fontSize(30)
191        .fontWeight(FontWeight.Bold)
192    }
193  }
194
195  aboutToAppear(): void {
196    setTimeout(() => {
197      this.builder_value = 'Hello World';
198    },3000)
199  }
200
201  build() {
202    Row() {
203      Column() {
204        Text(this.builder_value)
205          .fontSize(30)
206          .fontWeight(FontWeight.Bold)
207        this.builder()
208        Button('Click to change builder_value')
209          .onClick(() => {
210            this.builder_value = 'builder_value clicked'
211          })
212      }
213    }
214  }
215}
216```
217
218### Using Global Custom Builder Function
219
220Create a global \@Builder method and call this method by using **overBuilder()** in **Column**. Pass the simple type or complex type parameters in the form of object literals, value changes will trigger UI re-rendering.
221
222```ts
223class ChildTmp {
224  val: number = 1;
225}
226
227class Tmp {
228  str_value: string = 'Hello';
229  num_value: number = 0;
230  tmp_value: ChildTmp = new ChildTmp();
231  arrayTmp_value: Array<ChildTmp> = [];
232}
233
234@Builder function overBuilder(param: Tmp) {
235  Column() {
236    Text(`str_value: ${param.str_value}`)
237    Text(`num_value: ${param.num_value}`)
238    Text(`tmp_value: ${param.tmp_value.val}`)
239    ForEach(param.arrayTmp_value, (item: ChildTmp) => {
240      Text(`arrayTmp_value: ${item.val}`)
241    }, (item: ChildTmp) => JSON.stringify(item))
242  }
243}
244
245@Entry
246@Component
247struct Parent {
248  @State objParam: Tmp = new Tmp();
249  build() {
250    Column() {
251      Text('Render the UI by calling the @Builder')
252        .fontSize(20)
253      overBuilder({str_value: this.objParam.str_value, num_value: this.objParam.num_value, tmp_value: this.objParam.tmp_value, arrayTmp_value: this.objParam.arrayTmp_value})
254      Line()
255        .width('100%')
256        .height(10)
257        .backgroundColor('#000000').margin(10)
258      Button('Click to change parameter').onClick(() => {
259        this.objParam.str_value = 'Hello World';
260        this.objParam.num_value = 1;
261        this.objParam.tmp_value.val = 8;
262        const child_value: ChildTmp = {
263          val: 2
264        }
265        this.objParam.arrayTmp_value.push(child_value)
266      })
267    }
268  }
269}
270```
271
272### Changing the Variables Decorated by the Decorator Triggers UI Re-rendering
273
274In this case, the decorator feature is used. The change of the listening value triggers UI re-rendering and parameters are not passed through the \@Builder.
275
276```ts
277class Tmp {
278  str_value: string = 'Hello';
279}
280
281@Entry
282@Component
283struct Parent {
284  @State objParam: Tmp = new Tmp();
285  @State label: string = 'World';
286
287  @Builder privateBuilder() {
288    Column() {
289      Text(`wrapBuilder str_value: ${this.objParam.str_value}`)
290      Text(`wrapBuilder num: ${this.label}`)
291    }
292  }
293
294  build() {
295    Column() {
296      Text('Render the UI by calling the @Builder')
297        .fontSize(20)
298      this.privateBuilder()
299      Line()
300        .width('100%')
301        .height(10)
302        .backgroundColor('#000000').margin(10)
303      Button('Click to change parameter').onClick(() => {
304        this.objParam.str_value = 'str_value Hello World';
305        this.label = 'label Hello World'
306      })
307    }
308  }
309}
310```
311
312### Using the Global and Local @Builder to Pass in Parameters of the customBuilder Type
313
314```ts
315@Builder
316function overBuilder() {
317  Row() {
318    Text('Global Builder')
319      .fontSize(30)
320      .fontWeight(FontWeight.Bold)
321  }
322}
323
324@Entry
325@Component
326struct customBuilderDemo {
327  @State arr: number[] = [0, 1, 2, 3, 4];
328
329  @Builder privateBuilder() {
330    Row() {
331      Text('Local Builder')
332        .fontSize(30)
333        .fontWeight(FontWeight.Bold)
334    }
335  }
336
337  build() {
338    Column() {
339      List({ space: 10 }) {
340        ForEach(this.arr, (item: number) => {
341          ListItem(){
342            Text(`${item}`)
343              .width('100%')
344              .height(100)
345              .fontSize(16)
346              .textAlign(TextAlign.Center)
347              .borderRadius(10)
348              .backgroundColor(0xFFFFFF)
349          }
350            .swipeAction({
351              start: {
352                builder: overBuilder()
353              },
354              end: {
355                builder: () => { this.privateBuilder() }
356              }
357            })
358        }, (item: string) => JSON.stringify(item))
359      }
360    }
361  }
362}
363```
364
365### Nesting of Multi-layer \@Builder Method
366
367Call the custom components or other methods within \@Builder method. ArkUI provides [$$](arkts-two-way-sync.md) as a paradigm for passing parameters by reference.
368
369```ts
370class Tmp {
371  paramA1: string = '';
372}
373
374@Builder function parentBuilder($$: Tmp) {
375  Row() {
376    Column() {
377      Text(`parentBuilder===${$$.paramA1}`)
378        .fontSize(30)
379        .fontWeight(FontWeight.Bold)
380      HelloComponent({message: $$.paramA1})
381      childBuilder({paramA1: $$.paramA1})
382    }
383  }
384}
385
386@Component
387struct HelloComponent {
388  @Prop message: string = '';
389
390  build() {
391    Row() {
392      Text(`HelloComponent===${this.message}`)
393        .fontSize(30)
394        .fontWeight(FontWeight.Bold)
395    }
396  }
397}
398
399@Builder
400function childBuilder($$: Tmp) {
401  Row() {
402    Column() {
403      Text(`childBuilder===${$$.paramA1}`)
404        .fontSize(30)
405        .fontWeight(FontWeight.Bold)
406      HelloChildComponent({message: $$.paramA1})
407      grandsonBuilder({paramA1: $$.paramA1})
408    }
409  }
410}
411
412@Component
413struct HelloChildComponent {
414  @State message: string = '';
415  build() {
416    Row() {
417      Text(`HelloChildComponent===${this.message}`)
418        .fontSize(30)
419        .fontWeight(FontWeight.Bold)
420    }
421  }
422}
423
424@Builder function grandsonBuilder($$: Tmp) {
425  Row() {
426    Column() {
427      Text(`grandsonBuilder===${$$.paramA1}`)
428        .fontSize(30)
429        .fontWeight(FontWeight.Bold)
430      HelloGrandsonComponent({message: $$.paramA1})
431    }
432  }
433}
434
435@Component
436struct HelloGrandsonComponent {
437  @Prop message: string;
438  build() {
439    Row() {
440      Text(`HelloGrandsonComponent===${this.message}`)
441        .fontSize(30)
442        .fontWeight(FontWeight.Bold)
443    }
444  }
445}
446
447@Entry
448@Component
449struct Parent {
450  @State label: string = 'Hello';
451  build() {
452    Column() {
453      parentBuilder({paramA1: this.label})
454      Button('Click me').onClick(() => {
455        this.label = 'ArkUI';
456      })
457    }
458  }
459}
460```
461
462## FAQs
463
464### Two or More Parameters Are Used in the \@Builder
465
466When two or more parameters are used, the value change does not trigger the UI re-rendering even if the parameters are passed in the form of object literals.
467
468[Incorrect Example]
469
470```ts
471class GlobalTmp {
472  str_value: string = 'Hello';
473}
474
475@Builder function overBuilder(param: GlobalTmp, num: number) {
476  Column() {
477    Text(`str_value: ${param.str_value}`)
478    Text(`num: ${num}`)
479  }
480}
481
482@Entry
483@Component
484struct Parent {
485  @State objParam: GlobalTmp = new GlobalTmp();
486  @State num: number = 0;
487  build() {
488    Column() {
489      Text('Render the UI by calling the @Builder')
490        .fontSize(20)
491      overBuilder({str_value: this.objParam.str_value}, this.num) // Two parameters are used.
492      Line()
493        .width('100%')
494        .height(10)
495        .backgroundColor('#000000').margin(10)
496      Button('Click to change parameter').onClick(() => {
497        this.objParam.str_value = 'Hello World';
498        this.num = 1;
499      })
500    }
501  }
502}
503```
504
505[Incorrect Example]
506
507```ts
508class GlobalTmp {
509  str_value: string = 'Hello';
510}
511class SecondTmp {
512  num_value: number = 0;
513}
514@Builder function overBuilder(param: GlobalTmp, num: SecondTmp) {
515  Column() {
516    Text(`str_value: ${param.str_value}`)
517    Text(`num: ${num.num_value}`)
518  }
519}
520
521@Entry
522@Component
523struct Parent {
524  @State strParam: GlobalTmp = new GlobalTmp();
525  @State numParam: SecondTmp = new SecondTmp();
526  build() {
527    Column() {
528      Text('Render the UI by calling the @Builder')
529        .fontSize(20)
530      overBuilder({str_value: this.strParam.str_value}, {num_value: this.numParam.num_value}) // Two parameters are used.
531      Line()
532        .width('100%')
533        .height(10)
534        .backgroundColor('#000000').margin(10)
535      Button('Click to change parameter').onClick(() => {
536        this.strParam.str_value = 'Hello World';
537        this.numParam.num_value = 1;
538      })
539    }
540  }
541}
542```
543
544Only one parameter can be used in the \@Builder. When one parameter is passed in the form of object literals, the value change triggers the UI re-rendering.
545
546[Correct Example]
547
548```ts
549class GlobalTmp {
550  str_value: string = 'Hello';
551  num_value: number = 0;
552}
553@Builder function overBuilder(param: GlobalTmp) {
554  Column() {
555    Text(`str_value: ${param.str_value}`)
556    Text(`num: ${param.num_value}`)
557  }
558}
559
560@Entry
561@Component
562struct Parent {
563  @State objParam: GlobalTmp = new GlobalTmp();
564  build() {
565    Column() {
566      Text('Render the UI by calling the @Builder')
567        .fontSize(20)
568      overBuilder({str_value: this.objParam.str_value, num_value: this.objParam.num_value})
569      Line()
570        .width('100%')
571        .height(10)
572        .backgroundColor('#000000').margin(10)
573      Button('Click to change parameter').onClick(() => {
574        this.objParam.str_value = 'Hello World';
575        this.objParam.num_value = 1;
576      })
577    }
578  }
579}
580```
581