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