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