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