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