• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Custom Placeholder Node
2
3ArkUI provides ArkTS built-in components as placeholder nodes for custom nodes. These placeholder nodes have universal component attributes.
4
5## NodeContainer and NodeController
6
7[NodeContainer](../reference/apis-arkui/arkui-ts/ts-basic-components-nodecontainer.md), as a built-in component, only has universal component attributes, and its node layout follows the default top-left aligned [Stack](../reference/apis-arkui/arkui-ts/ts-container-stack.md) component. As a placeholder container, **NodeContainer** is primarily used for displaying custom nodes and for the display and reuse of custom node trees.
8
9[NodeController](../reference/apis-arkui/js-apis-arkui-nodeController.md) provides a set of lifecycle callbacks, including a **makeNode** callback that returns the root node of a **FrameNode** tree. This [FrameNode](../reference/apis-arkui/js-apis-arkui-frameNode.md) tree is then mounted under the corresponding **NodeContainer**. In addition, **NodeController** provides the following callback methods: **aboutToAppear**, **aboutToDisappear**, **aboutToResize**, **onTouchEvent**, and **rebuild**, which are used to listen for the status of the associated **NodeContainer**.
10
11For details about the callbacks, see [NodeController](../reference/apis-arkui/js-apis-arkui-nodeController.md).
12
13> **NOTE**
14>
15> - Only custom FrameNodes and root nodes of component trees created by **BuilderNode** are supported under the **NodeContainer**.
16>
17> - Since API version 12, you can obtain a built-in component's proxy node through the query API of the FrameNode. This proxy node can be returned as the result of the **makeNode** callback, but it cannot be successfully mounted on the component tree, resulting in a failed display of the proxy node.
18>
19> - A node must be used as the child of only one parent node to avoid display or functional issues, particularly in page routing and animation scenarios. For example, if a single node is mounted on multiple **NodeContainer**s through **NodeController**, only one of the **NodeContainer**s will display the node. In addition, any updates to attributes such as visibility and opacity in any of these **NodeContainer**s, which can affect the child component state, will all influence the mounted child node.
20
21```ts
22import { BuilderNode, FrameNode, NodeController, Size, UIContext } from '@kit.ArkUI'
23
24class Params {
25  text: string = "this is a text"
26}
27
28@Builder
29function buttonBuilder(params: Params) {
30  Column() {
31    Button(params.text)
32      .fontSize(12)
33      .borderRadius(8)
34      .borderWidth(2)
35      .backgroundColor(Color.Orange)
36  }
37}
38
39let buttonNode: BuilderNode<[Params]> | null = null;
40
41class MyNodeController extends NodeController {
42  private wrapBuilder: WrappedBuilder<[Params]> = wrapBuilder(buttonBuilder);
43  private isShow: boolean = false;
44
45  constructor(isShow: boolean) {
46    super();
47    this.isShow = isShow;
48  }
49
50  makeNode(uiContext: UIContext): FrameNode | null {
51    if (!this.isShow) {
52      return null;
53    }
54    if (buttonNode == null) {
55      buttonNode = new BuilderNode<[Params]>(uiContext);
56      buttonNode.build(this.wrapBuilder, { text: "This is a Button" })
57    }
58    let frameNode = buttonNode?.getFrameNode();
59    return frameNode ? frameNode : null;
60  }
61
62  aboutToResize(size: Size) {
63    console.log("aboutToResize width : " + size.width + " height : " + size.height)
64  }
65
66  aboutToAppear() {
67    console.log("aboutToAppear")
68  }
69
70  aboutToDisappear() {
71    console.log("aboutToDisappear");
72  }
73
74  onTouchEvent(event: TouchEvent) {
75    console.log("onTouchEvent");
76  }
77
78  toShow() {
79    this.isShow = true;
80    this.rebuild();
81  }
82
83  toHide() {
84    this.isShow = false;
85    this.rebuild();
86  }
87}
88
89@Entry
90@Component
91struct Index {
92  private myNodeController1: MyNodeController = new MyNodeController(true);
93  private myNodeController2: MyNodeController = new MyNodeController(false);
94
95  build() {
96    Column() {
97      NodeContainer(this.myNodeController1)
98        .width("100%")
99        .height("40%")
100        .backgroundColor(Color.Brown)
101      NodeContainer(this.myNodeController2)
102        .width("100%")
103        .height("40%")
104        .backgroundColor(Color.Gray)
105      Button("Change the place of button")
106        .onClick(() => {
107          // First, remove the node from the original placeholder node.
108          // Then, add the node to the new placeholder node.
109          // Ensure that the custom node only exists as the child of one node.
110          this.myNodeController1.toHide();
111          this.myNodeController2.toShow();
112        })
113    }
114    .padding({ left: 35, right: 35, top: 35 })
115    .width("100%")
116    .height("100%")
117  }
118}
119```
120