1# \@BuilderParam Decorator: @Builder Function Reference
2
3
4In certain circumstances, you may need to add a specific feature, such as a click-to-jump action, to a custom component. However, embedding an event method directly in a component will add the feature to all places where the component is imported. To solve this problem, ArkUI uses the \@BuilderParam decorator to decorate variables pointing to the [\@Builder](./arkts-builder.md) method (that is, @BuilderParam is used to decorate the @Builder function). When initializing a custom component, you can use different methods (such as modifying parameters, trailing closure, or using arrow function) to change values of the custom builder functions decorated by \@BuilderParam and call the \@BuilderParam in a custom component to add specific features. This decorator can be used to declare an element of any UI description, similar to a slot placeholder.
5
6
7> **NOTE**
8>
9> This decorator can be used in ArkTS widgets since API version 9.
10>
11> This decorator can be used in atomic services since API version 11.
12
13
14## Rules of Use
15
16
17### Initializing \@BuilderParam Decorated Methods
18
19An \@BuilderParam decorated method can be initialized only by an \@Builder function reference. If this decorator is used together with [\@Require](arkts-require.md) in API version 11, the parent component must construct input parameters.
20
21- Local initialization with the owning component's custom \@Builder function reference or a global \@Builder function reference
22
23  ```ts
24  @Builder function overBuilder() {}
25
26  @Component
27  struct Child {
28    @Builder doNothingBuilder() {};
29
30    // Use the custom builder function of the custom component for @BuilderParam initialization.
31    @BuilderParam customBuilderParam: () => void = this.doNothingBuilder;
32    // Use the global custom builder function for @BuilderParam initialization.
33    @BuilderParam customOverBuilderParam: () => void = overBuilder;
34    build(){}
35  }
36  ```
37
38- Initialization from the parent component
39
40  ```ts
41  @Component
42  struct Child {
43    @Builder customBuilder() {};
44    // Use the @Builder decorated method in the parent component for @BuilderParam initialization.
45    @BuilderParam customBuilderParam: () => void = this.customBuilder;
46
47    build() {
48      Column() {
49        this.customBuilderParam()
50      }
51    }
52  }
53
54  @Entry
55  @Component
56  struct Parent {
57    @Builder componentBuilder() {
58      Text(`Parent builder `)
59    }
60
61    build() {
62      Column() {
63        Child({ customBuilderParam: this.componentBuilder })
64      }
65    }
66  }
67  ```
68  **Figure 1** Example effect
69
70  ![builderparam-demo1](figures/builderparam-demo1.png)
71
72
73- **this** in the function body must point to the correct object.
74
75Example:
76
77  ```ts
78  @Component
79  struct Child {
80    label: string = `Child`;
81    @Builder customBuilder() {};
82    @Builder customChangeThisBuilder() {};
83    @BuilderParam customBuilderParam: () => void = this.customBuilder;
84    @BuilderParam customChangeThisBuilderParam: () => void = this.customChangeThisBuilder;
85
86    build() {
87      Column() {
88        this.customBuilderParam()
89        this.customChangeThisBuilderParam()
90      }
91    }
92  }
93
94  @Entry
95  @Component
96  struct Parent {
97    label: string = `Parent`;
98
99    @Builder componentBuilder() {
100      Text(`${this.label}`)
101    }
102
103    build() {
104      Column() {
105        // When this.componentBuilder() is called, this points to the Parent component decorated by the @Entry. That is, the value of the label variable is Parent.
106        this.componentBuilder()
107        Child({
108          // Pass this.componentBuilder to @BuilderParam customBuilderParam of the Child component. this points to the Child, that is, the value of the label variable is Child.
109          customBuilderParam: this.componentBuilder,
110          // Pass ():void=>{this.componentBuilder()} to @BuilderParam customChangeThisBuilderParam of the Child component.
111          // this of the arrow function points to the host object, so the value of the label variable is Parent.
112          customChangeThisBuilderParam: (): void => { this.componentBuilder() }
113        })
114      }
115    }
116  }
117  ```
118 **Figure 2** Example effect
119
120 ![builderparam-demo2](figures/builderparam-demo2.png)
121
122
123## Constraints
124
125- The \@BuilderParam decorated variable receives the \@Builder decorated function from the parent component. In addition, only local \@Builder function can be passed as a parameter.
126
127```ts
128@Component
129struct Child {
130  header: string = '';
131  @BuilderParam content: () => void;
132  footer: string = '';
133
134  build() {
135    Column() {
136      Text(this.header)
137      this.content();
138      Text(this.footer)
139    }
140  }
141}
142
143@Entry
144@Component
145struct Parent {
146  @Builder
147  test() {
148    Text('Hello')
149  }
150
151  build() {
152    Column() {
153      // Incorrect format. @BuilderParam needs to be initialized.
154      Child()
155      // Correct format.
156      Child({ content: this.test })
157    }
158  }
159}
160```
161
162- In the scenario where a custom component trailing closure is used, the child component has only one \@BuilderParam to receive this trailing closure, and the \@BuilderParam cannot contain parameters. For details, see [Component Initialization Through Trailing Closure](#component-initialization-through-trailing-closure).
163
164
165## Use Scenarios
166
167
168### Component Initialization Through Parameters
169
170An \@BuilderParam decorated method can be a method with or without parameters. Whether it contains parameters should match that of the assigned \@Builder method. The type of the \@BuilderParam decorated method must also match that of the assigned \@Builder method.
171
172```ts
173class Tmp{
174  label: string = '';
175}
176
177@Builder function overBuilder($$: Tmp) {
178  Text($$.label)
179    .width(400)
180    .height(50)
181    .backgroundColor(Color.Green)
182}
183
184@Component
185struct Child {
186  label: string = 'Child';
187  @Builder customBuilder() {};
188  // Without parameters. The pointed componentBuilder does not carry parameters either.
189  @BuilderParam customBuilderParam: () => void = this.customBuilder;
190  // With parameters. The pointed overBuilder also carries parameters.
191  @BuilderParam customOverBuilderParam: ($$: Tmp) => void = overBuilder;
192
193  build() {
194    Column() {
195      this.customBuilderParam()
196      this.customOverBuilderParam({label: 'global Builder label' } )
197    }
198  }
199}
200
201@Entry
202@Component
203struct Parent {
204  label: string = 'Parent';
205
206  @Builder componentBuilder() {
207    Text(`${this.label}`)
208  }
209
210  build() {
211    Column() {
212      this.componentBuilder()
213      Child({ customBuilderParam: this.componentBuilder, customOverBuilderParam: overBuilder })
214    }
215  }
216}
217```
218**Figure 3** Example effect
219
220![builderparam-demo3](figures/builderparam-demo3.png)
221
222
223### Component Initialization Through Trailing Closure
224
225In a custom component, the \@BuilderParam decorated attribute can be initialized using a trailing closure. During initialization, the component name is followed by a pair of braces ({}) to form a trailing closure.
226
227> **NOTE**
228>
229>  - In this scenario, the custom component can have only one \@BuilderParam decorated attribute.
230>
231>  - In this scenario, custom components do not support universal attributes.
232
233You can pass the content in the trailing closure to \@BuilderParam as an \@Builder decorated method.
234
235Example 1:
236
237```ts
238@Component
239struct CustomContainer {
240  @Prop header: string = '';
241  @Builder closerBuilder(){};
242  // Use the trailing closure {} (@Builder decorated method) of the parent component for @BuilderParam initialization.
243  @BuilderParam closer: () => void = this.closerBuilder;
244
245  build() {
246    Column() {
247      Text(this.header)
248        .fontSize(30)
249      this.closer()
250    }
251  }
252}
253
254@Builder function specificParam(label1: string, label2: string) {
255  Column() {
256    Text(label1)
257      .fontSize(30)
258    Text(label2)
259      .fontSize(30)
260  }
261}
262
263@Entry
264@Component
265struct CustomContainerUser {
266  @State text: string = 'header';
267
268  build() {
269    Column() {
270      // Create the CustomContainer component. During initialization, append a pair of braces ({}) to the component name to form a trailing closure.
271      // Used as the parameter passed to CustomContainer @BuilderParam closer: () => void.
272      CustomContainer({ header: this.text }) {
273        Column() {
274          specificParam('testA', 'testB')
275        }.backgroundColor(Color.Yellow)
276        .onClick(() => {
277          this.text = 'changeHeader';
278        })
279      }
280    }
281  }
282}
283```
284**Figure 4** Example effect
285
286![builderparam-demo4](figures/builderparam-demo4.png)
287
288Use global @Builder and local @Builder to initialize @BuilderParam in the @ComponentV2 decorated custom components through trailing closures.
289
290Example 2:
291
292```ts
293@ComponentV2
294struct ChildPage {
295  @Require @Param message: string = "";
296  @Builder customBuilder() {};
297  @BuilderParam customBuilderParam: () => void = this.customBuilder;
298
299  build() {
300    Column() {
301      Text(this.message)
302        .fontSize(30)
303        .fontWeight(FontWeight.Bold)
304      this.customBuilderParam()
305    }
306  }
307}
308
309const builder_value: string = 'Hello World';
310@Builder function overBuilder() {
311  Row() {
312    Text(`Global Builder: ${builder_value}`)
313      .fontSize(20)
314      .fontWeight(FontWeight.Bold)
315  }
316}
317
318@Entry
319@ComponentV2
320struct ParentPage {
321  @Local label: string = `Parent Page`;
322
323  @Builder componentBuilder() {
324    Row(){
325      Text(`Local Builder:${this.label}`)
326        .fontSize(20)
327        .fontWeight(FontWeight.Bold)
328    }
329  }
330
331  build() {
332    Column() {
333      ChildPage({ message: this.label}){
334        Column() {  // Use the local @Builder. Column component is followed by braces ({}) to form a trailing closure to initialize the custom component @BuilderParam.
335          this.componentBuilder();
336        }
337      }
338      Line()
339        .width('100%')
340        .height(10)
341        .backgroundColor('#000000').margin(10)
342      ChildPage({ message: this.label}){  // Use global @Builder. ChildPage component is followed by braces ({}) to form a trailing closure to initialize the custom component @BuilderParam.
343        Column() {
344          overBuilder();
345        }
346      }
347    }
348  }
349}
350```
351
352### \@BuilderParam Initialization Through Global and Local \@Builder
353
354In a custom component, the \@BuilderParam decorated variable is used to receive the content passed by the parent component through \@Builder for initialization. The \@Builder of the parent component can use the arrow function to change the object that this points to, therefore, when a \@BuilderParam decorated variable is used, different content is displayed.
355
356```ts
357@Component
358struct ChildPage {
359  label: string = `Child Page`;
360  @Builder customBuilder() {};
361  @BuilderParam customBuilderParam: () => void = this.customBuilder;
362  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
363
364  build() {
365    Column() {
366      this.customBuilderParam()
367      this.customChangeThisBuilderParam()
368    }
369  }
370}
371
372const builder_value: string = 'Hello World';
373@Builder function overBuilder() {
374  Row() {
375    Text(`Global Builder: ${builder_value}`)
376      .fontSize(20)
377      .fontWeight(FontWeight.Bold)
378  }
379}
380
381@Entry
382@Component
383struct ParentPage {
384  label: string = `Parent Page`;
385
386  @Builder componentBuilder() {
387    Row(){
388      Text(`Local Builder:${this.label}`)
389        .fontSize(20)
390        .fontWeight(FontWeight.Bold)
391    }
392  }
393
394  build() {
395    Column() {
396      // When this.componentBuilder() is called, this points to the **ParentPage** component decorated by the @Entry. Therefore, the value of the label variable is Parent Page.
397      this.componentBuilder()
398      ChildPage({
399        // Pass this.componentBuilder to @BuilderParam customBuilderParam of the ChildPage component. this points to ChildPage, that is, the value of the label variable is Child Page.
400        customBuilderParam: this.componentBuilder,
401        // Pass ():void=>{this.componentBuilder()} to @BuilderParam customChangeThisBuilderParam of the ChildPage component.
402        // this of the arrow function points to the host object, so the value of the label variable is Parent Page.
403        customChangeThisBuilderParam: (): void => { this.componentBuilder() }
404      })
405      Line()
406        .width('100%')
407        .height(10)
408        .backgroundColor('#000000').margin(10)
409      // When the global overBuilder() is called, this points to the entire current page. Therefore, the displayed content is Hello World.
410      overBuilder()
411      ChildPage({
412        // Pass the global overBuilder to @BuilderParam customBuilderParam of the ChildPage component. this points to the entire current page, that is, the displayed content is Hello World.
413        customBuilderParam: overBuilder,
414        // Pass the global overBuilder to @BuilderParam customChangeThisBuilderParam of the ChildPage component. this points to the entire current page, that is, the displayed content is Hello World.
415        customChangeThisBuilderParam: overBuilder
416      })
417    }
418  }
419}
420```
421**Figure 5** Example effect
422
423![builderparam-demo5](figures/builderparam-demo5.png)
424
425### Using @BuilderParam in a @ComponentV2 Decorated Custom Component
426
427Use global @Builder and local @Builder to initialize the @BuilderParam attribute of the @CompoentV2 decorated custom component.
428
429```ts
430@ComponentV2
431struct ChildPage {
432  @Param label: string = `Child Page`;
433  @Builder customBuilder() {};
434  @BuilderParam customBuilderParam: () => void = this.customBuilder;
435  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
436
437  build() {
438    Column() {
439      this.customBuilderParam()
440      this.customChangeThisBuilderParam()
441    }
442  }
443}
444
445const builder_value: string = 'Hello World';
446@Builder function overBuilder() {
447  Row() {
448    Text(`Global Builder: ${builder_value}`)
449      .fontSize(20)
450      .fontWeight(FontWeight.Bold)
451  }
452}
453
454@Entry
455@ComponentV2
456struct ParentPage {
457  @Local label: string = `Parent Page`;
458
459  @Builder componentBuilder() {
460    Row(){
461      Text(`Local Builder:${this.label}`)
462        .fontSize(20)
463        .fontWeight(FontWeight.Bold)
464    }
465  }
466
467  build() {
468    Column() {
469      // When this.componentBuilder() is called, this points to the **ParentPage** component decorated by the @Entry. Therefore, the value of the label variable is Parent Page.
470      this.componentBuilder()
471      ChildPage({
472        // Pass this.componentBuilder to @BuilderParam customBuilderParam of the ChildPage component. this points to ChildPage, that is, the value of the label variable is Child Page.
473        customBuilderParam: this.componentBuilder,
474        // Pass ():void=>{this.componentBuilder()} to @BuilderParam customChangeThisBuilderPara of the ChildPage component.
475        // this of the arrow function points to the host object, so the value of the label variable is Parent Page.
476        customChangeThisBuilderParam: (): void => { this.componentBuilder() }
477      })
478      Line()
479        .width('100%')
480        .height(5)
481        .backgroundColor('#000000').margin(10)
482      // When the global overBuilder() is called, this points to the entire current page. Therefore, the displayed content is Hello World.
483      overBuilder()
484      ChildPage({
485        // Pass the global overBuilder to @BuilderParam customBuilderParam of the ChildPage component. this points to the entire current page, that is, the displayed content is Hello World.
486        customBuilderParam: overBuilder,
487        // Pass the global overBuilder to @BuilderParam customChangeThisBuilderParam of the ChildPage component. this points to the entire current page, that is, the displayed content is Hello World.
488        customChangeThisBuilderParam: overBuilder
489      })
490    }
491  }
492}
493```
494**Figure 6** Example effect
495
496![builderparam-demo6](figures/builderparam-demo6.png)
497
498
499## FAQs
500
501### UI Re-rendering Fails When Content Is Changed
502
503When the custom component **ChildPage** is called, \@Builder is passed as a parameter through **this.componentBuilder**. Currently, this points to the custom component. Therefore, if the **label** value is changed inside the parent component, the custom component **ChildPage** cannot detect the change.
504
505[Incorrect Example]
506
507```ts
508@Component
509struct ChildPage {
510  @State label: string = `Child Page`;
511  @Builder customBuilder() {};
512  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
513
514  build() {
515    Column() {
516      this.customChangeThisBuilderParam()
517    }
518  }
519}
520
521@Entry
522@Component
523struct ParentPage {
524  @State label: string = `Parent Page`;
525
526  @Builder componentBuilder() {
527    Row(){
528      Text(`Builder :${this.label}`)
529        .fontSize(20)
530        .fontWeight(FontWeight.Bold)
531    }
532  }
533
534  build() {
535    Column() {
536      ChildPage({
537        // this points to the ChildPage component.
538        customChangeThisBuilderParam: this.componentBuilder
539      })
540      Button('Click to change label')
541        .onClick(() => {
542          this.label = 'Hello World';
543        })
544    }
545  }
546}
547```
548
549Use the arrow function to pass the \@Builder to the custom component **ChildPage** and this points to the parent component **ParentPage**. Therefore, if the value of label is changed in the parent component, the **ChildPage** detects the change and renders the UI again.
550The dynamic UI rendering can also be implemented by changing @Builder to @LocalBuilder.
551
552[Correct Example]
553
554```ts
555@Component
556struct ChildPage {
557  @State label: string = `Child Page`;
558  @Builder customBuilder() {};
559  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
560
561  build() {
562    Column() {
563      this.customChangeThisBuilderParam()
564    }
565  }
566}
567
568@Entry
569@Component
570struct ParentPage {
571  @State label: string = `Parent Page`;
572
573  @Builder componentBuilder() {
574    Row(){
575      Text(`Builder :${this.label}`)
576        .fontSize(20)
577        .fontWeight(FontWeight.Bold)
578    }
579  }
580
581  build() {
582    Column() {
583      ChildPage({
584        customChangeThisBuilderParam: () => { this.componentBuilder() }
585      })
586      Button('Click to change label')
587        .onClick(() => {
588          this.label = 'Hello World';
589        })
590    }
591  }
592}
593```
594