• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@BuilderParam Decorator: Referencing the \@Builder Function
2
3
4When you create a custom component and want to add a specific function, for example, a click-to-redirect operation, to a specified custom component, if you directly embed an event method into the component, the function will be added to all instances of the custom component. 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.
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    // Use the custom builder function of the custom component for @BuilderParam initialization.
32    @BuilderParam customBuilderParam: () => void = this.doNothingBuilder;
33    // Use the global custom builder function for @BuilderParam initialization.
34    @BuilderParam customOverBuilderParam: () => void = overBuilder;
35    build(){}
36  }
37  ```
38
39- Initialization from the parent component
40
41  ```ts
42  @Component
43  struct Child {
44    @Builder customBuilder() {};
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
75  Example:
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- \@BuilderParam decorated variables can be initialized only by using the \@Builder function. For details, see [Initialized Value of @BuilderParam Must Be @Builder](#initialized-value-of-builderparam-must-be-builder).
126
127- When the @Require and \@BuilderParam decorators are used together, the latter must be initialized. For details, see [Using @Require and @BuilderParam Together](#using-require-and-builderparam-together).
128
129- 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).
130
131## Use Scenarios
132
133### Component Initialization Through Parameters
134
135An \@BuilderParam decorated method can be a method with or without parameters. Whether it contains parameters should match that of the assigned \@Builder method.
136
137```ts
138class Tmp{
139  label: string = '';
140}
141
142@Builder function overBuilder($$: Tmp) {
143  Text($$.label)
144    .width(400)
145    .height(50)
146    .backgroundColor(Color.Green)
147}
148
149@Component
150struct Child {
151  label: string = 'Child';
152  @Builder customBuilder() {};
153  // Without parameters. The pointed customBuilder does not carry parameters either.
154  @BuilderParam customBuilderParam: () => void = this.customBuilder;
155  // With parameters. The pointed overBuilder also carries parameters.
156  @BuilderParam customOverBuilderParam: ($$: Tmp) => void = overBuilder;
157
158  build() {
159    Column() {
160      this.customBuilderParam()
161      this.customOverBuilderParam({label: 'global Builder label' } )
162    }
163  }
164}
165
166@Entry
167@Component
168struct Parent {
169  label: string = 'Parent';
170
171  @Builder componentBuilder() {
172    Text(`${this.label}`)
173  }
174
175  build() {
176    Column() {
177      this.componentBuilder()
178      Child({ customBuilderParam: this.componentBuilder, customOverBuilderParam: overBuilder })
179    }
180  }
181}
182```
183**Figure 3** Example effect
184
185![builderparam-demo3](figures/builderparam-demo3.png)
186
187
188### Component Initialization Through Trailing Closure
189
190In 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.
191
192> **NOTE**
193>
194>  - In this scenario, the custom component can have only one \@BuilderParam decorated attribute.
195>
196>  - In this scenario, custom components do not support universal attributes.
197
198You can pass the content in the trailing closure to \@BuilderParam as an \@Builder decorated method.
199
200Example 1:
201
202```ts
203@Component
204struct CustomContainer {
205  @Prop header: string = '';
206  @Builder closerBuilder(){};
207  // Use the trailing closure {} (@Builder decorated method) of the parent component for @BuilderParam initialization.
208  @BuilderParam closer: () => void = this.closerBuilder;
209
210  build() {
211    Column() {
212      Text(this.header)
213        .fontSize(30)
214      this.closer()
215    }
216  }
217}
218
219@Builder function specificParam(label1: string, label2: string) {
220  Column() {
221    Text(label1)
222      .fontSize(30)
223    Text(label2)
224      .fontSize(30)
225  }
226}
227
228@Entry
229@Component
230struct CustomContainerUser {
231  @State text: string = 'header';
232
233  build() {
234    Column() {
235      // Create the CustomContainer component. During initialization, append a pair of braces ({}) to the component name to form a trailing closure.
236      // Used as the parameter passed to CustomContainer @BuilderParam closer: () => void.
237      CustomContainer({ header: this.text }) {
238        Column() {
239          specificParam('testA', 'testB')
240        }.backgroundColor(Color.Yellow)
241        .onClick(() => {
242          this.text = 'changeHeader';
243        })
244      }
245    }
246  }
247}
248```
249**Figure 4** Example effect
250
251![builderparam-demo4](figures/builderparam-demo4.png)
252
253Use global @Builder and local @Builder to initialize @BuilderParam in the @ComponentV2 decorated custom components through trailing closures.
254
255Example 2:
256
257```ts
258@ComponentV2
259struct ChildPage {
260  @Require @Param message: string = "";
261  @Builder customBuilder() {};
262  @BuilderParam customBuilderParam: () => void = this.customBuilder;
263
264  build() {
265    Column() {
266      Text(this.message)
267        .fontSize(30)
268        .fontWeight(FontWeight.Bold)
269      this.customBuilderParam()
270    }
271  }
272}
273
274const builder_value: string = 'Hello World';
275@Builder function overBuilder() {
276  Row() {
277    Text(`Global Builder: ${builder_value}`)
278      .fontSize(20)
279      .fontWeight(FontWeight.Bold)
280  }
281}
282
283@Entry
284@ComponentV2
285struct ParentPage {
286  @Local label: string = 'Parent Page';
287
288  @Builder componentBuilder() {
289    Row(){
290      Text(`Local Builder:${this.label}`)
291        .fontSize(20)
292        .fontWeight(FontWeight.Bold)
293    }
294  }
295
296  build() {
297    Column() {
298      ChildPage({ message: this.label}){
299        Column() {  // Use the local @Builder. Column component is followed by braces ({}) to form a trailing closure to initialize the custom component @BuilderParam.
300          this.componentBuilder();
301        }
302      }
303      Line()
304        .width('100%')
305        .height(10)
306        .backgroundColor('#000000').margin(10)
307      ChildPage({ message: this.label}){  // Use global @Builder. ChildPage component is followed by braces ({}) to form a trailing closure to initialize the custom component @BuilderParam.
308        Column() {
309          overBuilder();
310        }
311      }
312    }
313  }
314}
315```
316
317### \@BuilderParam Initialization Through Global and Local \@Builder
318
319In 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.
320
321```ts
322@Component
323struct ChildPage {
324  label: string = 'Child Page';
325  @Builder customBuilder() {};
326  @BuilderParam customBuilderParam: () => void = this.customBuilder;
327  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
328
329  build() {
330    Column() {
331      this.customBuilderParam()
332      this.customChangeThisBuilderParam()
333    }
334  }
335}
336
337const builder_value: string = 'Hello World';
338@Builder function overBuilder() {
339  Row() {
340    Text(`Global Builder: ${builder_value}`)
341      .fontSize(20)
342      .fontWeight(FontWeight.Bold)
343  }
344}
345
346@Entry
347@Component
348struct ParentPage {
349  label: string = 'Parent Page';
350
351  @Builder componentBuilder() {
352    Row(){
353      Text(`Local Builder:${this.label}`)
354        .fontSize(20)
355        .fontWeight(FontWeight.Bold)
356    }
357  }
358
359  build() {
360    Column() {
361      // 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.
362      this.componentBuilder()
363      ChildPage({
364        // 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.
365        customBuilderParam: this.componentBuilder,
366        // Pass ():void=>{this.componentBuilder()} to @BuilderParam customChangeThisBuilderParam of the ChildPage component.
367        // this of the arrow function points to the host object, so the value of the label variable is Parent Page.
368        customChangeThisBuilderParam: (): void => { this.componentBuilder() }
369      })
370      Line()
371        .width('100%')
372        .height(10)
373        .backgroundColor('#000000').margin(10)
374      // When the global overBuilder() is called, this points to the entire current page. Therefore, the displayed content is Hello World.
375      overBuilder()
376      ChildPage({
377        // 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.
378        customBuilderParam: overBuilder,
379        // 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.
380        customChangeThisBuilderParam: overBuilder
381      })
382    }
383  }
384}
385```
386**Figure 5** Example effect
387
388![builderparam-demo5](figures/builderparam-demo5.png)
389
390### Using @BuilderParam in a @ComponentV2 Decorated Custom Component
391
392Use global @Builder and local @Builder to initialize the @BuilderParam attribute of the @ComponentV2 decorated custom component.
393
394```ts
395@ComponentV2
396struct ChildPage {
397  @Param label: string = 'Child Page';
398  @Builder customBuilder() {};
399  @BuilderParam customBuilderParam: () => void = this.customBuilder;
400  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
401
402  build() {
403    Column() {
404      this.customBuilderParam()
405      this.customChangeThisBuilderParam()
406    }
407  }
408}
409
410const builder_value: string = 'Hello World';
411@Builder function overBuilder() {
412  Row() {
413    Text(`Global Builder: ${builder_value}`)
414      .fontSize(20)
415      .fontWeight(FontWeight.Bold)
416  }
417}
418
419@Entry
420@ComponentV2
421struct ParentPage {
422  @Local label: string = 'Parent Page';
423
424  @Builder componentBuilder() {
425    Row(){
426      Text(`Local Builder:${this.label}`)
427        .fontSize(20)
428        .fontWeight(FontWeight.Bold)
429    }
430  }
431
432  build() {
433    Column() {
434      // 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.
435      this.componentBuilder()
436      ChildPage({
437        // 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.
438        customBuilderParam: this.componentBuilder,
439        // Pass ():void=>{this.componentBuilder()} to @BuilderParam customChangeThisBuilderPara of the ChildPage component.
440        // this of the arrow function points to the host object, so the value of the label variable is Parent Page.
441        customChangeThisBuilderParam: (): void => { this.componentBuilder() }
442      })
443      Line()
444        .width('100%')
445        .height(5)
446        .backgroundColor('#000000').margin(10)
447      // When the global overBuilder() is called, this points to the entire current page. Therefore, the displayed content is Hello World.
448      overBuilder()
449      ChildPage({
450        // 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.
451        customBuilderParam: overBuilder,
452        // 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.
453        customChangeThisBuilderParam: overBuilder
454      })
455    }
456  }
457}
458```
459**Figure 6** Example effect
460
461![builderparam-demo6](figures/builderparam-demo6.png)
462
463
464## FAQs
465
466### UI Re-rendering Fails When Content Is Changed
467
468When 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.
469
470[Incorrect Example]
471
472```ts
473@Component
474struct ChildPage {
475  @State label: string = 'Child Page';
476  @Builder customBuilder() {};
477  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
478
479  build() {
480    Column() {
481      this.customChangeThisBuilderParam()
482    }
483  }
484}
485
486@Entry
487@Component
488struct ParentPage {
489  @State label: string = 'Parent Page';
490
491  @Builder componentBuilder() {
492    Row(){
493      Text(`Builder :${this.label}`)
494        .fontSize(20)
495        .fontWeight(FontWeight.Bold)
496    }
497  }
498
499  build() {
500    Column() {
501      ChildPage({
502        // this points to the ChildPage component.
503        customChangeThisBuilderParam: this.componentBuilder
504      })
505      Button('Click to change label')
506        .onClick(() => {
507          this.label = 'Hello World';
508        })
509    }
510  }
511}
512```
513
514Use 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.
515The dynamic UI rendering can also be implemented by changing @Builder to @LocalBuilder.
516
517[Correct Example]
518
519```ts
520@Component
521struct ChildPage {
522  @State label: string = 'Child Page';
523  @Builder customBuilder() {};
524  @BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
525
526  build() {
527    Column() {
528      this.customChangeThisBuilderParam()
529    }
530  }
531}
532
533@Entry
534@Component
535struct ParentPage {
536  @State label: string = 'Parent Page';
537
538  @Builder componentBuilder() {
539    Row(){
540      Text(`Builder :${this.label}`)
541        .fontSize(20)
542        .fontWeight(FontWeight.Bold)
543    }
544  }
545
546  build() {
547    Column() {
548      ChildPage({
549        customChangeThisBuilderParam: () => { this.componentBuilder() }
550      })
551      Button('Click to change label')
552        .onClick(() => {
553          this.label = 'Hello World';
554        })
555    }
556  }
557}
558```
559
560### Using @Require and @BuilderParam Together
561
562The variables decorated by @Require need to be initialized; otherwise, an error is reported during compilation.
563
564[Incorrect Example]
565
566```ts
567@Builder function globalBuilder() {
568  Text('Hello World')
569}
570
571@Entry
572@Component
573struct customBuilderDemo {
574  build() {
575    Column() {
576      // No value is assigned to ChildBuilder. An error is reported during compilation or editing.
577      ChildPage()
578    }
579  }
580}
581
582@Component
583struct ChildPage {
584  @Require @BuilderParam ChildBuilder: () => void = globalBuilder;
585  build() {
586    Column() {
587      this.ChildBuilder()
588    }
589  }
590}
591```
592
593Initialize the variables decorated by @Require. In this case, no error is reported during compilation.
594
595[Correct Example]
596
597```ts
598@Builder function globalBuilder() {
599  Text('Hello World')
600}
601
602@Entry
603@Component
604struct customBuilderDemo {
605  build() {
606    Column() {
607      ChildPage({ChildBuilder: globalBuilder})
608    }
609  }
610}
611
612@Component
613struct ChildPage {
614  @Require @BuilderParam ChildBuilder: () => void = globalBuilder;
615  build() {
616    Column() {
617      this.ChildBuilder()
618    }
619  }
620}
621```
622
623### Initialized Value of @BuilderParam Must Be @Builder
624
625Use the @State decorated variables to initialize the variables of @BuilderParam decorated child component **ChildBuilder**. An error is reported during compilation.
626
627[Incorrect Example]
628
629```ts
630@Builder function globalBuilder() {
631  Text('Hello World')
632}
633@Entry
634@Component
635struct customBuilderDemo {
636  @State message: string = "";
637  build() {
638    Column() {
639      // ChildBuilder receives the variable decorated by @State. An error is reported during compilation or editing.
640      ChildPage({ChildBuilder: this.message})
641    }
642  }
643}
644
645@Component
646struct ChildPage {
647  @BuilderParam ChildBuilder: () => void = globalBuilder;
648  build() {
649    Column() {
650      this.ChildBuilder()
651    }
652  }
653}
654```
655
656Use @Builder decorated **globalBuilder()** to initialize the variables of @BuilderParam decorated **ChildBuilder**. No error is reported during compilation.
657
658[Correct Example]
659
660```ts
661@Builder function globalBuilder() {
662  Text('Hello World')
663}
664@Entry
665@Component
666struct customBuilderDemo {
667  build() {
668    Column() {
669      ChildPage({ChildBuilder: globalBuilder})
670    }
671  }
672}
673
674@Component
675struct ChildPage {
676  @BuilderParam ChildBuilder: () => void = globalBuilder;
677  build() {
678    Column() {
679      this.ChildBuilder()
680    }
681  }
682}
683```
684