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  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  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 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 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 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 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