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