1# BuilderNode 2 3The **BuilderNode** module provides APIs for creating a BuilderNode – a custom node that can be used to mount native components. A BuilderNode can be used only as a leaf node. Whenever possible, avoid operating child nodes and their attributes under the root node through the root node's render node. 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 "@ohos.arkui.node"; 15``` 16 17## NodeRenderType 18 19Enumerates the node rendering types. 20 21**System capability**: SystemCapability.ArkUI.ArkUI.Full 22 23| Name | Value | Description | 24| ------------------- | --- | ---------------------------- | 25| RENDER_TYPE_DISPLAY | 0 | The node is displayed on the screen.| 26| RENDER_TYPE_TEXTURE | 1 | The node is exported as a texture. | 27 28## RenderOptions 29 30Provides optional parameters for creating a BuilderNode. 31 32**System capability**: SystemCapability.ArkUI.ArkUI.Full 33 34| Name | Type | Mandatory| Description | 35| ------------- | -------------------------------------- | ---- | ------------------------------------------------------------ | 36| selfIdealSize | [Size](js-apis-arkui-graphics.md#size) | No | Ideal size of the node. | 37| type | [NodeRenderType](#noderendertype) | No | Rendering type of the node. | 38| surfaceId | string | No | Surface ID of the texture receiver. Generally, the texture receiver is an [OH_NativeImage](../native-apis/_o_h___native_image.md#oh_nativeimage) instance.| 39 40## BuilderNode 41 42class BuilderNode<Args extends Object[]> 43 44Implements 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). 45 46**System capability**: SystemCapability.ArkUI.ArkUI.Full 47 48### constructor 49 50constructor(uiContext: UIContext, options?: RenderOptions) 51 52Constructor 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]. 53 54**System capability**: SystemCapability.ArkUI.ArkUI.Full 55 56| Name | Type | Mandatory| Description | 57| --------- | --------------------------------------- | ---- | ----------------------------------------------------------------- | 58| 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).| 59| options | [RenderOptions](#renderoptions) | No | Parameters for creating a BuilderNode. | 60 61### build 62 63build(builder: WrappedBuilder\<Args>, arg?: Object): void 64 65Creates 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. 66Custom components are allowed. Yet, the custom components cannot use decorators, such as [@Reusable](../../quick-start/arkts-create-custom-components.md#basic-usage-of-custom-components), [@Link](../../quick-start/arkts-link.md), [@Prop](../../quick-start/arkts-prop.md), [@Provide](../../quick-start/arkts-provide-and-consume.md), and [@Consume](../../quick-start/arkts-provide-and-consume.md), for state synchronization with the owning page. 67 68**System capability**: SystemCapability.ArkUI.ArkUI.Full 69 70**Parameters** 71 72| Name | Type | Mandatory| Description | 73| ------- | --------------------------------------------------------------- | ---- | -------------------------------------------------------------------------------------- | 74| builder | [WrappedBuilder\<Args>](../../quick-start/arkts-wrapBuilder.md) | Yes | Stateless UI method [@Builder](../../quick-start/arkts-builder.md) required for creating a component tree.| 75| arg | Object | No | Object, which is used as the input parameter of the builder. | 76 77 78### getFrameNode 79 80getFrameNode(): FrameNode | null 81 82Obtains the FrameNode in the BuilderNode. The FrameNode is generated only after the BuilderNode executes the build operation. 83 84**System capability**: SystemCapability.ArkUI.ArkUI.Full 85 86**Return value** 87 88| Type | Description | 89| --------------------------------------------------------- | --------------------------------------------------------------------- | 90| [FrameNode](js-apis-arkui-frameNode.md#framenode) \| null | **FrameNode** object. If no such object is held by the **BuilderNode** instance, null is returned.| 91 92**Example 1** 93 94In this example, the BuilderNode is returned as the root node of the **\<NodeContainer>**. 95 96```ts 97import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node" 98import { UIContext } from '@ohos.arkui.UIContext'; 99 100class Params { 101 text: string = "" 102 constructor(text: string) { 103 this.text = text; 104 } 105} 106 107@Builder 108function buildText(params: Params) { 109 Column() { 110 Text(params.text) 111 .fontSize(50) 112 .fontWeight(FontWeight.Bold) 113 .margin({bottom: 36}) 114 } 115} 116 117class TextNodeController extends NodeController { 118 private textNode: BuilderNode<[Params]> | null = null; 119 private message: string = "DEFAULT"; 120 121 constructor(message: string) { 122 super(); 123 this.message = message; 124 } 125 126 makeNode(context: UIContext): FrameNode | null { 127 this.textNode = new BuilderNode(context); 128 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 129 130 return this.textNode.getFrameNode(); 131 } 132} 133 134@Entry 135@Component 136struct Index { 137 @State message: string = "hello" 138 139 build() { 140 Row() { 141 Column() { 142 NodeContainer(new TextNodeController(this.message)) 143 .width('100%') 144 .height(100) 145 .backgroundColor('#FFF0F0F0') 146 } 147 .width('100%') 148 .height('100%') 149 } 150 .height('100%') 151 } 152} 153``` 154 155**Example 2** 156 157This example mounts the RenderNode of the BuilderNode to another RenderNode. 158 159```ts 160import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node" 161import { UIContext } from '@ohos.arkui.UIContext'; 162 163class Params { 164 text: string = "" 165 166 constructor(text: string) { 167 this.text = text; 168 } 169} 170 171@Builder 172function buildText(params: Params) { 173 Column() { 174 Text(params.text) 175 .fontSize(50) 176 .fontWeight(FontWeight.Bold) 177 .margin({ bottom: 36 }) 178 } 179} 180 181class TextNodeController extends NodeController { 182 private rootNode: FrameNode | null = null; 183 private textNode: BuilderNode<[Params]> | null = null; 184 private message: string = "DEFAULT"; 185 186 constructor(message: string) { 187 super(); 188 this.message = message; 189 } 190 191 makeNode(context: UIContext): FrameNode | null { 192 this.rootNode = new FrameNode(context); 193 194 this.textNode = new BuilderNode(context, { selfIdealSize: { width: 150, height: 150 } }); 195 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)); 196 const textRenderNode = this.textNode?.getFrameNode()?.getRenderNode(); 197 198 const rootRenderNode = this.rootNode.getRenderNode(); 199 if (rootRenderNode !== null) { 200 rootRenderNode.appendChild(textRenderNode); 201 } 202 203 return this.rootNode; 204 } 205} 206 207@Entry 208@Component 209struct Index { 210 @State message: string = "hello" 211 212 build() { 213 Row() { 214 Column() { 215 NodeContainer(new TextNodeController(this.message)) 216 .width('100%') 217 .height(100) 218 .backgroundColor('#FFF0F0F0') 219 } 220 .width('100%') 221 .height('100%') 222 } 223 .height('100%') 224 } 225} 226``` 227 228### update 229 230update(arg: Object): void 231 232Updates 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. 233 234**System capability**: SystemCapability.ArkUI.ArkUI.Full 235 236**Parameters** 237 238| Name| Type | Mandatory| Description | 239| ------ | ------ | ---- | ------------------------------------------------------------------------ | 240| arg | Object | Yes | Parameter used to update the BuilderNode. It is of the same type as the parameter passed to the [build](###build) API.| 241 242**Example** 243```ts 244import { UIContext } from '@ohos.arkui.UIContext'; 245import { NodeController, BuilderNode, FrameNode } from "@ohos.arkui.node" 246 247class Params { 248 text: string = "" 249 constructor(text: string) { 250 this.text = text; 251 } 252} 253 254// Custom component 255@Component 256struct TextBuilder { 257 @Prop message: string = "TextBuilder"; 258 259 build() { 260 Row() { 261 Column() { 262 Text(this.message) 263 .fontSize(50) 264 .fontWeight(FontWeight.Bold) 265 .margin({bottom: 36}) 266 .backgroundColor(Color.Gray) 267 } 268 } 269 } 270} 271 272@Builder 273function buildText(params: Params) { 274 Column() { 275 Text(params.text) 276 .fontSize(50) 277 .fontWeight(FontWeight.Bold) 278 .margin({ bottom: 36 }) 279 TextBuilder({message: params.text}) //Custom component 280 } 281} 282 283class TextNodeController extends NodeController { 284 private rootNode: FrameNode | null = null; 285 private textNode: BuilderNode<[Params]> | null = null; 286 private message: string = ""; 287 288 constructor(message: string) { 289 super() 290 this.message = message 291 } 292 293 makeNode(context: UIContext): FrameNode | null { 294 this.textNode = new BuilderNode(context); 295 this.textNode.build(wrapBuilder<[Params]>(buildText), new Params(this.message)) 296 return this.textNode.getFrameNode(); 297 } 298 299 update(message: string) { 300 if (this.textNode !== null) { 301 this.textNode.update(new Params(message)); 302 } 303 } 304} 305 306@Entry 307@Component 308struct Index { 309 @State message: string = "hello" 310 private textNodeController: TextNodeController = new TextNodeController(this.message); 311 private count = 0; 312 313 build() { 314 Row() { 315 Column() { 316 NodeContainer(this.textNodeController) 317 .width('100%') 318 .height(100) 319 .backgroundColor('#FFF0F0F0') 320 Button('Update') 321 .onClick(() => { 322 this.count += 1; 323 const message = "Update " + this.count.toString(); 324 this.textNodeController.update(message); 325 }) 326 } 327 .width('100%') 328 .height('100%') 329 } 330 .height('100%') 331 } 332} 333``` 334 335### postTouchEvent 336 337postTouchEvent(event: TouchEvent): boolean 338 339Dispatches an event to the FrameNode created by this BuilderNode. 340 341**System capability**: SystemCapability.ArkUI.ArkUI.Full 342 343**Parameters** 344 345| Name| Type | Mandatory| Description | 346| ------ | ------------------------------------------------------------------------- | ---- | ---------- | 347| event | [TouchEvent](../arkui-ts/ts-universal-events-touch.md#touchevent) | Yes | Touch event.| 348 349**Return value** 350 351| Type | Description | 352| ------- | ------------------ | 353| boolean | Whether the event is successfully dispatched.| 354 355**Example** 356 357```ts 358import { UIContext } from '@ohos.arkui.UIContext'; 359import { NodeController, BuilderNode, FrameNode } from '@ohos.arkui.node'; 360 361class Params { 362 text: string = "this is a text" 363} 364 365@Builder 366function ButtonBuilder(params: Params) { 367 Column() { 368 Button(`button ` + params.text) 369 .borderWidth(2) 370 .backgroundColor(Color.Orange) 371 .width("100%") 372 .height("100%") 373 .gesture( 374 TapGesture() 375 .onAction((event: GestureEvent) => { 376 console.log("TapGesture"); 377 }) 378 ) 379 } 380 .width(500) 381 .height(300) 382 .backgroundColor(Color.Gray) 383} 384 385class MyNodeController extends NodeController { 386 private rootNode: BuilderNode<[Params]> | null = null; 387 private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(ButtonBuilder); 388 389 makeNode(uiContext: UIContext): FrameNode | null { 390 this.rootNode = new BuilderNode(uiContext); 391 this.rootNode.build(this.wrapBuilder, { text: "this is a string" }) 392 return this.rootNode.getFrameNode(); 393 } 394 395 postTouchEvent(touchEvent: TouchEvent): void { 396 if(this.rootNode == null){ 397 return; 398 } 399 let result = this.rootNode.postTouchEvent(touchEvent); 400 console.log("result " + result); 401 } 402} 403 404@Entry 405@Component 406struct MyComponent { 407 private nodeController: MyNodeController = new MyNodeController(); 408 409 build() { 410 Column() { 411 NodeContainer(this.nodeController) 412 .height(300) 413 .width(500) 414 415 Column() 416 .width(500) 417 .height(300) 418 .backgroundColor(Color.Pink) 419 .onTouch((event) => { 420 if(event != undefined){ 421 this.nodeController.postTouchEvent(event); 422 } 423 }) 424 } 425 } 426} 427``` 428