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