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