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