1# BuilderNode 2 3The **BuilderNode** module provides APIs for a BuilderNode, a custom node that can be used to mount native components. A BuilderNode can be used only as a leaf node. For details, see [BuilderNode Development](../../ui/arkts-user-defined-arktsNode-builderNode.md). 4 5> **NOTE** 6> 7> The initial APIs of this module are supported since API version 11. Newly added APIs will be marked with a superscript to indicate their earliest API version. 8> 9> **BuilderNode** is not available in DevEco Studio Previewer. 10 11## Modules to Import 12 13```ts 14import { BuilderNode, RenderOptions, NodeRenderType } from "@kit.ArkUI"; 15``` 16 17## NodeRenderType 18 19Enumerates the node rendering types. 20 21**Atomic service API**: This API can be used in atomic services since API version 12. 22 23**System capability**: SystemCapability.ArkUI.ArkUI.Full 24 25| Name | Value | Description | 26| ------------------- | --- | ---------------------------- | 27| RENDER_TYPE_DISPLAY | 0 | The node is displayed on the screen.| 28| RENDER_TYPE_TEXTURE | 1 | The node is exported as a texture. | 29 30> **NOTE** 31> 32> Currently, the **RENDER_TYPE_TEXTURE** type takes effect only for the [XComponentNode](./js-apis-arkui-xcomponentNode.md) and the [BuilderNode](#buildernode-1) holding a component tree whose root node is a custom component. 33> 34> In the case of [BuilderNode](#buildernode-1), the following custom components that function as the root node support texture export: Badge, Blank, Button, CanvasGradient, CanvasPattern, CanvasRenderingContext2D, Canvas, CheckboxGroup, Checkbox, Circle, ColumnSplit, Column, ContainerSpan, Counter, DataPanel, Divider, Ellipse, Flex, Gauge, Hyperlink, ImageBitmap, ImageData, Image, Line, LoadingProgress, Marquee, Matrix2D, OffscreenCanvasRenderingContext2D, OffscreenCanvas, Path2D, Path, PatternLock, Polygon, Polyline, Progress, QRCode, Radio, Rating, Rect, RelativeContainer, RowSplit, Row, Shape, Slider, Span, Stack, TextArea, TextClock, TextInput, TextTimer, Text, Toggle, Video (not supporting the native full-screen mode), Web, XComponent 35> 36> The following components support texture export since API version 12: DatePicker, ForEach, Grid, IfElse, LazyForEach, List, Scroll, Swiper, TimePicker, @Component decorated custom components, NodeContainer, and FrameNode and RenderNode mounted to a NodeContainer. 37> 38> For details, see [Rendering and Drawing Video and Button Components at the Same Layer](../../web/web-same-layer.md). 39 40## RenderOptions 41 42Provides optional parameters for creating a BuilderNode. 43 44**Atomic service API**: This API can be used in atomic services since API version 12. 45 46**System capability**: SystemCapability.ArkUI.ArkUI.Full 47 48| Name | Type | Mandatory| Description | 49| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 50| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | No | Ideal size of the node. | 51| type | [NodeRenderType](#noderendertype) | No | Rendering type of the node. | 52| surfaceId | string | No | Surface ID of the texture receiver. Generally, the texture receiver is an [OH_NativeImage](../apis-arkgraphics2d/_o_h___native_image.md#oh_nativeimage) instance.| 53 54## BuilderNode 55 56class BuilderNode\<Args extends Object[]> 57 58Implements a BuilderNode, which can create a component tree through the stateless UI method [@Builder](../../quick-start/arkts-builder.md) and hold the root node of the component tree. A BuilderNode cannot be defined as a state variable. The FrameNode held in the BuilderNode is only used to mount the BuilderNode to other FrameNodes as a child node. Undefined behavior may occur if you set attributes or perform operations on subnodes of the FrameNode held by the BuilderNode. Therefore, after you have obtained a [RenderNode](js-apis-arkui-renderNode.md#rendernode) through the [getFrameNode](#getframenode) method of the BuilderNode and the [getRenderNode](js-apis-arkui-frameNode.md#getrendernode) method of the [FrameNode](js-apis-arkui-frameNode.md#framenode), avoid setting the attributes or operating the subnodes through APIs of the [RenderNode](js-apis-arkui-renderNode.md#rendernode). 59 60**Atomic service API**: This API can be used in atomic services since API version 12. 61 62**System capability**: SystemCapability.ArkUI.ArkUI.Full 63 64### constructor 65 66constructor(uiContext: UIContext, options?: RenderOptions) 67 68Constructor for creating a BuilderNode. When the content generated by the BuilderNode is embedded in another RenderNode for display, that is, the RenderNode corresponding to the BuilderNode is mounted to another RenderNode for display, **selfIdealSize** in **RenderOptions** must be explicitly specified. If **selfIdealSize** is not set, the node in the builder follows the default parent component layout constraint [0, 0], which means that the size of the root node of the subtree in BuilderNode is [0, 0]. 69 70**Atomic service API**: This API can be used in atomic services since API version 12. 71 72**System capability**: SystemCapability.ArkUI.ArkUI.Full 73 74| Name | Type | Mandatory| Description | 75| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- | 76| uiContext | [UIContext](js-apis-arkui-UIContext.md) | Yes | UI context. For details about how to obtain it, see [[Obtaining UI Context](./js-apis-arkui-node.md#obtaining-ui-context).| 77| options | [RenderOptions](#renderoptions) | No | Parameters for creating a BuilderNode. | 78 79> **NOTE** 80> The input parameter for **uiContext** must be a valid value, that is, the UI context must be correct. If an invalid value is passed in or if no value is specified, creation will fail. 81 82### build 83 84build(builder: WrappedBuilder\<Args>, arg?: Object): void 85 86Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method [@Builder](../../quick-start/arkts-builder.md) has at most one root node. 87Custom components are allowed. Yet, the custom components cannot use decorators, such as [@Reusable](../../quick-start/arkts-create-custom-components.md#basic-structure-of-a-custom-component), @Link, @Provide, and @Consume, for state synchronization with the owning page. 88 89> **NOTE** 90> 91> When nesting @Builder, ensure that the input objects for the inner and outer @Builder methods are consistent. 92> 93> The outermost @Builder supports only one input argument. 94 95**Atomic service API**: This API can be used in atomic services since API version 12. 96 97**System capability**: SystemCapability.ArkUI.ArkUI.Full 98 99**Parameters** 100 101| Name | Type | Mandatory| Description | 102| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 103| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | Yes | Stateless UI method [@Builder](../../quick-start/arkts-builder.md) required for creating a component tree.| 104| arg | Object | No | Argument of the builder. Only one input argument is supported, and the type of the input argument must be consistent with the type defined by @Builder. | 105 106 107### BuildOptions<sup>12+</sup> 108 109Defines the optional build options. 110 111**Atomic service API**: This API can be used in atomic services since API version 12. 112 113**System capability**: SystemCapability.ArkUI.ArkUI.Full 114 115| Name | Type | Mandatory| Description | 116| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 117| nestingBuilderSupported |boolean | No | Whether to support nesting **@Builder** within **@Builder**. The value **false** means that the input arguments for **@Builder** are consistent, and **true** means the opposite.<br> Default value: **false** | 118 119### build<sup>12+</sup> 120 121build(builder: WrappedBuilder\<Args>, arg: Object, options: [BuildOptions](#buildoptions12)): void 122 123Creates a component tree based on the passed object and holds the root node of the component tree. The stateless UI method [@Builder](../../quick-start/arkts-builder.md) has at most one root node. 124Custom components are allowed. Yet, the custom components cannot use decorators, such as [@Reusable](../../quick-start/arkts-create-custom-components.md#basic-structure-of-a-custom-component), @Link, @Provide, and @Consume, for state synchronization with the owning page. 125 126> **NOTE** 127> 128> For details about the creation and update using @Builder, see [@Builder](../../quick-start/arkts-builder.md). 129> 130> The outermost @Builder supports only one input argument. 131 132**Atomic service API**: This API can be used in atomic services since API version 12. 133 134**System capability**: SystemCapability.ArkUI.ArkUI.Full 135 136**Parameters** 137 138| Name | Type | Mandatory| Description | 139| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 140| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | Yes | Stateless UI method [@Builder](../../quick-start/arkts-builder.md) required for creating a component tree. | 141| arg | Object | Yes | Argument of the builder. Only one input argument is supported, and the type of the input argument must be consistent with the type defined by @Builder. | 142| options | BuildOptions | Yes | Build options, which determine whether to support nesting **@Builder** within **@Builder**. | 143 144**Example** 145```ts 146import { BuilderNode, NodeContent } from "@kit.ArkUI" 147 148interface ParamsInterface { 149 text: string; 150 func: Function; 151} 152 153@Builder 154function buildTextWithFunc(fun: Function) { 155 Text(fun()) 156 .fontSize(50) 157 .fontWeight(FontWeight.Bold) 158 .margin({ bottom: 36 }) 159} 160 161@Builder 162function buildText(params: ParamsInterface) { 163 Column() { 164 Text(params.text) 165 .fontSize(50) 166 .fontWeight(FontWeight.Bold) 167 .margin({ bottom: 36 }) 168 buildTextWithFunc(params.func) 169 } 170} 171 172 173@Entry 174@Component 175struct Index { 176 @State message: string = "HELLO" 177 private content: NodeContent = new NodeContent(); 178 179 build() { 180 Row() { 181 Column() { 182 Button('addBuilderNode') 183 .onClick(() => { 184 let buildNode = new BuilderNode<[ParamsInterface]>(this.getUIContext()); 185 buildNode.build(wrapBuilder<[ParamsInterface]>(buildText), { 186 text: this.message, func: () => { 187 return "FUNCTION" 188 } 189 }, { nestingBuilderSupported: true }); 190 this.content.addFrameNode(buildNode.getFrameNode()); 191 buildNode.dispose(); 192 }) 193 ContentSlot(this.content) 194 } 195 .id("column") 196 .width('100%') 197 .height('100%') 198 } 199 .height('100%') 200 } 201} 202``` 203 204 205### getFrameNode 206 207getFrameNode(): FrameNode | null 208 209Obtains the FrameNode in the BuilderNode. The FrameNode is generated only after the BuilderNode executes the build operation. 210 211**Atomic service API**: This API can be used in atomic services since API version 12. 212 213**System capability**: SystemCapability.ArkUI.ArkUI.Full 214 215**Return value** 216 217| Type | Description | 218| --------------------------------------------------------- | --------------------------------------------------------------------- | 219| [FrameNode](js-apis-arkui-frameNode.md#framenode) \| null | **FrameNode** object. If no such object is held by the **BuilderNode** instance, null is returned.| 220 221**Example 1** 222 223In this example, the BuilderNode is returned as the root node of the **\<NodeContainer>**. 224 225```ts 226import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI" 227 228class Params { 229 text: string = "" 230 constructor(text: string) { 231 this.text = text; 232 } 233} 234 235@Builder 236function buildText(params: Params) { 237 Column() { 238 Text(params.text) 239 .fontSize(50) 240 .fontWeight(FontWeight.Bold) 241 .margin({bottom: 36}) 242 } 243} 244 245class TextNodeController extends NodeController { 246 private textNode: BuilderNode<[Params]> | null = null; 247 private message: string = "DEFAULT"; 248 249 constructor(message: string) { 250 super(); 251 this.message = message; 252 } 253 254 makeNode(context: UIContext): FrameNode | null { 255 this.textNode = new BuilderNode(context); 256 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 257 258 return this.textNode.getFrameNode(); 259 } 260} 261 262@Entry 263@Component 264struct Index { 265 @State message: string = "hello" 266 267 build() { 268 Row() { 269 Column() { 270 NodeContainer(new TextNodeController(this.message)) 271 .width('100%') 272 .height(100) 273 .backgroundColor('#FFF0F0F0') 274 } 275 .width('100%') 276 .height('100%') 277 } 278 .height('100%') 279 } 280} 281``` 282 283**Example 2** 284 285This example shows how to mount a FrameNode within a BuilderNode to another FrameNode. 286 287```ts 288import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI" 289 290class Params { 291 text: string = "" 292 293 constructor(text: string) { 294 this.text = text; 295 } 296} 297 298@Builder 299function buildText(params: Params) { 300 Column() { 301 Text(params.text) 302 .fontSize(50) 303 .fontWeight(FontWeight.Bold) 304 .margin({ bottom: 36 }) 305 } 306} 307 308class TextNodeController extends NodeController { 309 private rootNode: FrameNode | null = null; 310 private textNode: BuilderNode<[Params]> | null = null; 311 private message: string = "DEFAULT"; 312 313 constructor(message: string) { 314 super(); 315 this.message = message; 316 } 317 318 makeNode(context: UIContext): FrameNode | null { 319 this.rootNode = new FrameNode(context); 320 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 321 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 322 if (this.rootNode !== null) { 323 this.rootNode.appendChild(this.textNode?.getFrameNode()); 324 } 325 326 return this.rootNode; 327 } 328} 329 330@Entry 331@Component 332struct Index { 333 @State message: string = "hello" 334 335 build() { 336 Row() { 337 Column() { 338 NodeContainer(new TextNodeController(this.message)) 339 .width('100%') 340 .height(100) 341 .backgroundColor('#FFF0F0F0') 342 } 343 .width('100%') 344 .height('100%') 345 } 346 .height('100%') 347 } 348} 349``` 350 351**Example 3** 352 353This example shows how to mount a BuilderNode's RenderNode under another RenderNode. Since the RenderNode does not pass layout constraints, this mode of mounting nodes is not recommended. 354 355```ts 356import { NodeController, BuilderNode, FrameNode, UIContext, RenderNode } from "@kit.ArkUI" 357 358class Params { 359 text: string = "" 360 361 constructor(text: string) { 362 this.text = text; 363 } 364} 365 366@Builder 367function buildText(params: Params) { 368 Column() { 369 Text(params.text) 370 .fontSize(50) 371 .fontWeight(FontWeight.Bold) 372 .margin({ bottom: 36 }) 373 } 374} 375 376class TextNodeController extends NodeController { 377 private rootNode: FrameNode | null = null; 378 private textNode: BuilderNode<[Params]> | null = null; 379 private message: string = "DEFAULT"; 380 381 constructor(message: string) { 382 super(); 383 this.message = message; 384 } 385 386 makeNode(context: UIContext): FrameNode | null { 387 this.rootNode = new FrameNode(context); 388 let renderNode = new RenderNode(); 389 renderNode.clipToFrame = false; 390 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 391 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 392 const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode(); 393 394 const rootRenderNode = this.rootNode.getRenderNode(); 395 if (rootRenderNode !== null) { 396 rootRenderNode.appendChild(renderNode); 397 renderNode.appendChild(textRenderNode); 398 } 399 400 return this.rootNode; 401 } 402} 403 404@Entry 405@Component 406struct Index { 407 @State message: string = "hello" 408 409 build() { 410 Row() { 411 Column() { 412 NodeContainer(new TextNodeController(this.message)) 413 .width('100%') 414 .height(100) 415 .backgroundColor('#FFF0F0F0') 416 } 417 .width('100%') 418 .height('100%') 419 } 420 .height('100%') 421 } 422} 423``` 424 425### update 426 427update(arg: Object): void 428 429Updates this BuilderNode based on the provided parameter, which is of the same type as the input parameter passed to the [build](#build) API. To call this API on a custom component, the variable used in the component must be defined as the @Prop type. 430 431**Atomic service API**: This API can be used in atomic services since API version 12. 432 433**System capability**: SystemCapability.ArkUI.ArkUI.Full 434 435**Parameters** 436 437| Name| Type | Mandatory| Description | 438| ------ | ------ | ---- | ------------------------------------------------------------------------ | 439| arg | Object | Yes | Parameter used to update the BuilderNode. It is of the same type as the parameter passed to the [build](#build) API.| 440 441**Example** 442```ts 443import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI" 444 445class Params { 446 text: string = "" 447 constructor(text: string) { 448 this.text = text; 449 } 450} 451 452// Custom component 453@Component 454struct TextBuilder { 455 @Prop message: string = "TextBuilder"; 456 457 build() { 458 Row() { 459 Column() { 460 Text(this.message) 461 .fontSize(50) 462 .fontWeight(FontWeight.Bold) 463 .margin({bottom: 36}) 464 .backgroundColor(Color.Gray) 465 } 466 } 467 } 468} 469 470@Builder 471function buildText(params: Params) { 472 Column() { 473 Text(params.text) 474 .fontSize(50) 475 .fontWeight(FontWeight.Bold) 476 .margin({ bottom: 36 }) 477 TextBuilder({message: params.text}) // Custom component 478 } 479} 480 481class TextNodeController extends NodeController { 482 private rootNode: FrameNode | null = null; 483 private textNode: BuilderNode<[Params]> | null = null; 484 private message: string = ""; 485 486 constructor(message: string) { 487 super() 488 this.message = message 489 } 490 491 makeNode(context: UIContext): FrameNode | null { 492 this.textNode = new BuilderNode(context); 493 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 494 return this.textNode.getFrameNode(); 495 } 496 497 update(message: string) { 498 if (this.textNode !== null) { 499 this.textNode.update(new Params(message)); 500 } 501 } 502} 503 504@Entry 505@Component 506struct Index { 507 @State message: string = "hello" 508 private textNodeController: TextNodeController = new TextNodeController(this.message); 509 private count = 0; 510 511 build() { 512 Row() { 513 Column() { 514 NodeContainer(this.textNodeController) 515 .width('100%') 516 .height(200) 517 .backgroundColor('#FFF0F0F0') 518 Button('Update') 519 .onClick(() => { 520 this.count += 1; 521 const message = "Update " + this.count.toString(); 522 this.textNodeController.update(message); 523 }) 524 } 525 .width('100%') 526 .height('100%') 527 } 528 .height('100%') 529 } 530} 531``` 532 533### postTouchEvent 534 535postTouchEvent(event: TouchEvent): boolean 536 537Dispatches an event to the FrameNode created by this BuilderNode. 538 539**Atomic service API**: This API can be used in atomic services since API version 12. 540 541**System capability**: SystemCapability.ArkUI.ArkUI.Full 542 543**Parameters** 544 545| Name| Type | Mandatory| Description | 546| ------ | ------------------------------------------------------------------------- | ---- | ---------- | 547| event | [TouchEvent](arkui-ts/ts-universal-events-touch.md#touchevent) | Yes | Touch event.| 548 549**Return value** 550 551| Type | Description | 552| ------- | ------------------ | 553| boolean | Whether the event is successfully dispatched.| 554 555**Example** 556 557```ts 558import { NodeController, BuilderNode, FrameNode, UIContext } from '@kit.ArkUI'; 559 560class Params { 561 text: string = "this is a text" 562} 563 564@Builder 565function ButtonBuilder(params: Params) { 566 Column() { 567 Button(`button ` + params.text) 568 .borderWidth(2) 569 .backgroundColor(Color.Orange) 570 .width("100%") 571 .height("100%") 572 .gesture( 573 TapGesture() 574 .onAction((event: GestureEvent) => { 575 console.log("TapGesture"); 576 }) 577 ) 578 } 579 .width(500) 580 .height(300) 581 .backgroundColor(Color.Gray) 582} 583 584class MyNodeController extends NodeController { 585 private rootNode: BuilderNode<[Params]> | null = null; 586 private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder); 587 588 makeNode(uiContext: UIContext): FrameNode | null { 589 this.rootNode = new BuilderNode(uiContext); 590 this.rootNode.build(this.wrapBuilder, { text: "this is a string" }) 591 return this.rootNode.getFrameNode(); 592 } 593 594 postTouchEvent(touchEvent: TouchEvent): void { 595 if(this.rootNode == null){ 596 return; 597 } 598 let result = this.rootNode.postTouchEvent(touchEvent); 599 console.log("result " + result); 600 } 601} 602 603@Entry 604@Component 605struct MyComponent { 606 private nodeController: MyNodeController = new MyNodeController(); 607 608 build() { 609 Column() { 610 NodeContainer(this.nodeController) 611 .height(300) 612 .width(500) 613 614 Column() 615 .width(500) 616 .height(300) 617 .backgroundColor(Color.Pink) 618 .onTouch((event) => { 619 if(event != undefined){ 620 this.nodeController.postTouchEvent(event); 621 } 622 }) 623 } 624 } 625} 626``` 627 628### dispose<sup>12+</sup> 629 630dispose(): void 631 632Releases this BuilderNode immediately. Calling **dispose** on a **BuilderNode** object breaks its reference to the backend entity node, and also simultaneously severs the references of its contained FrameNode and RenderNode to their respective entity nodes. 633 634**Atomic service API**: This API can be used in atomic services since API version 12. 635 636**System capability**: SystemCapability.ArkUI.ArkUI.Full 637 638```ts 639import { RenderNode, FrameNode, NodeController, BuilderNode } from "@kit.ArkUI" 640 641@Component 642struct TestComponent { 643 build() { 644 Column() { 645 Text('This is a BuilderNode.') 646 .fontSize(16) 647 .fontWeight(FontWeight.Bold) 648 } 649 .width('100%') 650 .backgroundColor(Color.Gray) 651 } 652 653 aboutToAppear() { 654 console.error('aboutToAppear'); 655 } 656 657 aboutToDisappear() { 658 console.error('aboutToDisappear'); 659 } 660} 661 662@Builder 663function buildComponent() { 664 TestComponent() 665} 666 667class MyNodeController extends NodeController { 668 private rootNode: FrameNode | null = null; 669 private builderNode: BuilderNode<[]> | null = null; 670 671 makeNode(uiContext: UIContext): FrameNode | null { 672 this.rootNode = new FrameNode(uiContext); 673 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 200, height: 100 } }); 674 this.builderNode.build(new WrappedBuilder(buildComponent)); 675 676 const rootRenderNode = this.rootNode!.getRenderNode(); 677 if (rootRenderNode !== null) { 678 rootRenderNode.size = { width: 200, height: 200 }; 679 rootRenderNode.backgroundColor = 0xff00ff00; 680 rootRenderNode.appendChild(this.builderNode!.getFrameNode()!.getRenderNode()); 681 } 682 683 return this.rootNode; 684 } 685 686 dispose() { 687 if (this.builderNode !== null) { 688 this.builderNode.dispose(); 689 } 690 } 691 692 removeBuilderNode() { 693 const rootRenderNode = this.rootNode!.getRenderNode(); 694 if (rootRenderNode !== null && this.builderNode !== null && this.builderNode.getFrameNode() !== null) { 695 rootRenderNode.removeChild(this.builderNode!.getFrameNode()!.getRenderNode()); 696 } 697 } 698} 699 700@Entry 701@Component 702struct Index { 703 private myNodeController: MyNodeController = new MyNodeController(); 704 705 build() { 706 Column({ space: 4 }) { 707 NodeContainer(this.myNodeController) 708 Button('BuilderNode dispose') 709 .onClick(() => { 710 this.myNodeController.removeBuilderNode(); 711 this.myNodeController.dispose(); 712 }) 713 .width('100%') 714 } 715 } 716} 717``` 718 719### reuse<sup>12+</sup> 720 721reuse(param?: Object): void 722 723Passes the reuse event to the custom component in this BuilderNode. 724 725**Atomic service API**: This API can be used in atomic services since API version 12. 726 727**System capability**: SystemCapability.ArkUI.ArkUI.Full 728 729**Parameters** 730 731| Name| Type | Mandatory| Description | 732| ------ | ------ | ---- | ------------------------------------------------------------------------ | 733| param | Object | No | Parameter used to reuse the BuilderNode. It is of the same type as the parameter passed to the [build](#build) API.| 734 735### recycle<sup>12+</sup> 736 737recycle(): void 738 739Passes the recycle event to the custom component in this BuiderNode. 740 741**Atomic service API**: This API can be used in atomic services since API version 12. 742 743**System capability**: SystemCapability.ArkUI.ArkUI.Full 744 745```ts 746import { FrameNode,NodeController,BuilderNode,UIContext } from "@kit.ArkUI" 747 748class MyDataSource { 749 private dataArray: string[] = []; 750 private listener: DataChangeListener | null = null 751 752 public totalCount(): number { 753 return this.dataArray.length; 754 } 755 756 public getData(index: number) { 757 return this.dataArray[index]; 758 } 759 760 public pushData(data: string) { 761 this.dataArray.push(data); 762 } 763 764 public reloadListener(): void { 765 this.listener?.onDataReloaded(); 766 } 767 768 public registerDataChangeListener(listener: DataChangeListener): void { 769 this.listener = listener; 770 } 771 772 public unregisterDataChangeListener(): void { 773 this.listener = null; 774 } 775} 776 777class Params { 778 item: string = ''; 779 780 constructor(item: string) { 781 this.item = item; 782 } 783} 784 785@Builder 786function buildNode(param: Params = new Params("hello")) { 787 ReusableChildComponent2({ item: param.item }); 788} 789 790class MyNodeController extends NodeController { 791 public builderNode: BuilderNode<[Params]> | null = null; 792 public item: string = ""; 793 794 makeNode(uiContext: UIContext): FrameNode | null { 795 if (this.builderNode == null) { 796 this.builderNode = new BuilderNode(uiContext, { selfIdealSize: { width: 300, height: 200 } }); 797 this.builderNode.build(wrapBuilder<[Params]>(buildNode), new Params(this.item)); 798 } 799 return this.builderNode.getFrameNode(); 800 } 801} 802 803@Reusable 804@Component 805struct ReusableChildComponent { 806 @State item: string = ''; 807 private controller: MyNodeController = new MyNodeController(); 808 809 aboutToAppear() { 810 this.controller.item = this.item; 811 } 812 813 aboutToRecycle(): void { 814 console.log("ReusableChildComponent aboutToRecycle " + this.item); 815 this.controller?.builderNode?.recycle(); 816 } 817 818 aboutToReuse(params: object): void { 819 console.log("ReusableChildComponent aboutToReuse " + JSON.stringify(params)); 820 this.controller?.builderNode?.reuse(params); 821 } 822 823 build() { 824 NodeContainer(this.controller); 825 } 826} 827 828@Component 829struct ReusableChildComponent2 { 830 @Prop item: string = "false"; 831 832 aboutToReuse(params: Record<string, object>) { 833 console.log("ReusableChildComponent2 Reusable 2 " + JSON.stringify(params)); 834 } 835 836 aboutToRecycle(): void { 837 console.log("ReusableChildComponent2 aboutToRecycle 2 " + this.item); 838 } 839 840 build() { 841 Row() { 842 Text(this.item) 843 .fontSize(20) 844 .backgroundColor(Color.Yellow) 845 .margin({ left: 10 }) 846 }.margin({ left: 10, right: 10 }) 847 } 848} 849 850 851@Entry 852@Component 853struct Index { 854 @State data: MyDataSource = new MyDataSource(); 855 856 aboutToAppear() { 857 for (let i = 0;i < 100; i++) { 858 this.data.pushData(i.toString()); 859 } 860 } 861 862 build() { 863 Column() { 864 List({ space: 3 }) { 865 LazyForEach(this.data, (item: string) => { 866 ListItem() { 867 ReusableChildComponent({ item: item }) 868 } 869 }, (item: string) => item) 870 } 871 .width('100%') 872 .height('100%') 873 } 874 } 875} 876``` 877 878### updateConfiguration<sup>12+</sup> 879 880updateConfiguration(): void 881 882Updates the configuration of the entire node by passing in a [system environment change](../apis-ability-kit/js-apis-app-ability-configuration.md) event. 883 884**Atomic service API**: This API can be used in atomic services since API version 12. 885 886**System capability**: SystemCapability.ArkUI.ArkUI.Full 887 888> **NOTE** 889> 890> The **updateConfiguration** API is used to instruct an object to update, with the system environment used for the update being determined by the changes in the application's current system environment. 891 892**Example** 893```ts 894import { NodeController, BuilderNode, FrameNode, UIContext } from "@kit.ArkUI" 895import { AbilityConstant, Configuration, EnvironmentCallback } from '@kit.AbilityKit'; 896 897class Params { 898 text: string = "" 899 900 constructor(text: string) { 901 this.text = text; 902 } 903} 904 905// Custom component 906@Component 907struct TextBuilder { 908 // The @Prop decorated attribute is the attribute to be updated in the custom component. It is a basic attribute. 909 @Prop message: string = "TextBuilder"; 910 911 build() { 912 Row() { 913 Column() { 914 Text(this.message) 915 .fontSize(50) 916 .fontWeight(FontWeight.Bold) 917 .margin({ bottom: 36 }) 918 .fontColor($r(`app.color.text_color`)) 919 .backgroundColor($r(`app.color.start_window_background`)) 920 } 921 } 922 } 923} 924 925@Builder 926function buildText(params: Params) { 927 Column() { 928 Text(params.text) 929 .fontSize(50) 930 .fontWeight(FontWeight.Bold) 931 .margin({ bottom: 36 }) 932 .fontColor($r(`app.color.text_color`)) 933 TextBuilder({ message: params.text }) // Custom component 934 }.backgroundColor($r(`app.color.start_window_background`)) 935} 936 937class TextNodeController extends NodeController { 938 private textNode: BuilderNode<[Params]> | null = null; 939 private message: string = ""; 940 941 constructor(message: string) { 942 super() 943 this.message = message; 944 } 945 946 makeNode(context: UIContext): FrameNode | null { 947 return this.textNode?.getFrameNode() ? this.textNode?.getFrameNode() : null; 948 } 949 950 createNode(context: UIContext) { 951 this.textNode = new BuilderNode(context); 952 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 953 builderNodeMap.push(this.textNode); 954 } 955 956 deleteNode() { 957 let node = builderNodeMap.pop(); 958 node?.dispose(); 959 } 960 961 update(message: string) { 962 if (this.textNode !== null) { 963 // Call update to perform an update. 964 this.textNode.update(new Params(message)); 965 } 966 } 967} 968 969// Record the created custom node object. 970const builderNodeMap: Array<BuilderNode<[Params]>> = new Array(); 971 972function updateColorMode() { 973 builderNodeMap.forEach((value, index) => { 974 // Notify BuilderNode of the environment changes. 975 value.updateConfiguration(); 976 }) 977} 978 979@Entry 980@Component 981struct Index { 982 @State message: string = "hello" 983 private textNodeController: TextNodeController = new TextNodeController(this.message); 984 private count = 0; 985 986 aboutToAppear(): void { 987 let environmentCallback: EnvironmentCallback = { 988 onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { 989 console.log('onMemoryLevel'); 990 }, 991 onConfigurationUpdated: (config: Configuration): void => { 992 console.log('onConfigurationUpdated ' + JSON.stringify(config)); 993 updateColorMode(); 994 } 995 } 996 // Register a callback. 997 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); 998 // Create a custom node and add it to the map. 999 this.textNodeController.createNode(this.getUIContext()); 1000 } 1001 1002 aboutToDisappear(): void { 1003 // Remove the reference to the custom node from the map and release the node. 1004 this.textNodeController.deleteNode(); 1005 } 1006 1007 build() { 1008 Row() { 1009 Column() { 1010 NodeContainer(this.textNodeController) 1011 .width('100%') 1012 .height(200) 1013 .backgroundColor('#FFF0F0F0') 1014 Button('Update') 1015 .onClick(() => { 1016 this.count += 1; 1017 const message = "Update " + this.count.toString(); 1018 this.textNodeController.update(message); 1019 }) 1020 } 1021 .width('100%') 1022 .height('100%') 1023 } 1024 .height('100%') 1025 } 1026} 1027``` 1028