1# Migrating Web Components Between Different Windows 2 3The **Web** component can be attached to and detached from the component trees in different windows, which enables the same **Web** component to be migrated between different windows. For example, you can drag a tab page to an independent window or drag it to another window. 4 5Web components are migrated between different windows based on the [Custom Node](../ui/arkts-user-defined-node.md) capability. You can use [BuilderNode](../ui/arkts-user-defined-arktsNode-builderNode.md) to create offline nodes for **Web** components and use [Custom Placeholder Node](../ui/arkts-user-defined-place-hoder.md) to attach and detach web nodes. The **Web** component is migrated between windows by detaching the web node from one window and attaching it to another window. 6 7In the following example, a **Web** component is created using a command when the main window Ability is started. You can use the functions and classes provided in **common.ets** to attach and detach the **Web** component. In addition, **Index.ets** provides an implementation method for attaching and detaching **Web** components. In this way, you can attach and detach **Web** components in different windows, in other words, migrate **Web** components between different windows. The following figure shows the migration process. 8 9 10 11> **NOTE** 12> 13> Do not attach a **Web** component under two parent nodes at the same time. Otherwise, unexpected behavior occurs. 14 15```ts 16// Main window ability. 17// EntryAbility.ets 18import { createNWeb, defaultUrl } from '../pages/common' 19 20// ... 21 22 onWindowStageCreate(windowStage: window.WindowStage): void { 23 hilog.info(0x0000, 'testTag', '%{public}s', 'Ability onWindowStageCreate'); 24 25 windowStage.loadContent('pages/Index', (err) => { 26 if (err.code) { 27 hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? ''); 28 return; 29 } 30 // Create a dynamic Web component, in which UIContext should be passed. (The component can be created at any time after loadContent() is called, and only one Web component is created for the application.) 31 createNWeb(defaultUrl, windowStage.getMainWindowSync().getUIContext()); 32 hilog.info(0x0000, 'testTag', 'Succeeded in loading the content.'); 33 }); 34 } 35 36// ... 37``` 38 39```ts 40// Provide the capability for attaching Web components dynamically. 41// pages/common.ets 42import { UIContext, NodeController, BuilderNode, FrameNode } from '@kit.ArkUI'; 43import { webview } from '@kit.ArkWeb'; 44import { hilog } from '@kit.PerformanceAnalysisKit'; 45 46export const defaultUrl : string = 'https://www.example.com'; 47 48// Data is an input parameter of encapsulation class. 49class Data{ 50 url: string = ''; 51 webController: webview.WebviewController | null = null; 52 53 constructor(url: string, webController: webview.WebviewController) { 54 this.url = url; 55 this.webController = webController; 56 } 57} 58 59// @Builder contains the specific information of the dynamic component. 60@Builder 61function WebBuilder(data:Data) { 62 Web({ src: data.url, controller: data.webController }) 63 .width("100%") 64 .height("100%") 65 .borderStyle(BorderStyle.Dashed) 66 .borderWidth(2) 67} 68 69let wrap = wrapBuilder<[Data]>(WebBuilder); 70 71// Used to control and report the behavior of the node in NodeContainer. This function must be used together with NodeContainer. 72export class MyNodeController extends NodeController { 73 private builderNode: BuilderNode<[Data]> | null | undefined = null; 74 private webController : webview.WebviewController | null | undefined = null; 75 private rootNode : FrameNode | null = null; 76 77 constructor(builderNode : BuilderNode<[Data]> | undefined, webController : webview.WebviewController | undefined) { 78 super(); 79 this.builderNode = builderNode; 80 this.webController = webController; 81 } 82 83 // This function must be overridden, which is used to construct the number of nodes, return the nodes and attach them to NodeContainer. 84 // Call it or rebuild() to refresh when NodeContainer is created. 85 makeNode(uiContext: UIContext): FrameNode | null { 86 // This node will be attached to the parent node of NodeContainer. 87 return this.rootNode; 88 } 89 90 // Attach the Webview. 91 attachWeb() : void { 92 if (this.builderNode) { 93 let frameNode : FrameNode | null = this.builderNode.getFrameNode(); 94 if (frameNode?.getParent() != null) { 95 // Check whether the custom node is attached before. 96 hilog.error(0x0000, 'testTag', '%{public}s', 'The frameNode is already attached'); 97 return; 98 } 99 this.rootNode = this.builderNode.getFrameNode(); 100 } 101 } 102 103 // Detach the Webview. 104 detachWeb() : void { 105 this.rootNode = null; 106 } 107 108 getWebController() : webview.WebviewController | null | undefined { 109 return this.webController; 110 } 111} 112 113// Create a BuilderNode required for saving Map. 114let builderNodeMap : Map<string, BuilderNode<[Data]> | undefined> = new Map(); 115// Create a webview.WebviewController required for saving Map. 116let webControllerMap : Map<string, webview.WebviewController | undefined> = new Map(); 117 118// Use getUIContext() of the window or custom component to obtain the UIContext object required for initialization. 119export const createNWeb = (url: string, uiContext: UIContext) => { 120 // Create a WebviewController. 121 let webController = new webview.WebviewController() ; 122 // Create a BuilderNode. 123 let builderNode : BuilderNode<[Data]> = new BuilderNode(uiContext); 124 // Create a dynamic Web component. 125 builderNode.build(wrap, new Data(url, webController)); 126 127 // Save the BuilderNode. 128 builderNodeMap.set(url, builderNode); 129 // Save the WebviewController. 130 webControllerMap.set(url, webController); 131} 132 133// Customize the API for obtaining BuilderNode. 134export const getBuilderNode = (url : string) : BuilderNode<[Data]> | undefined => { 135 return builderNodeMap.get(url); 136} 137// Customize the API for obtaining WebviewController. 138export const getWebviewController = (url : string) : webview.WebviewController | undefined => { 139 return webControllerMap.get(url); 140} 141 142``` 143 144```ts 145// Use the pages of NodeController. 146// pages/Index.ets 147import { getBuilderNode, MyNodeController, defaultUrl, getWebviewController } from "./common" 148 149@Entry 150@Component 151struct Index { 152 private nodeController : MyNodeController = 153 new MyNodeController(getBuilderNode(defaultUrl), getWebviewController(defaultUrl)); 154 155 build() { 156 Row() { 157 Column() { 158 Button("Attach Webview") 159 .onClick(() => { 160 // Do not attach the same node to different pages at the same time. 161 this.nodeController.attachWeb(); 162 this.nodeController.rebuild(); 163 }) 164 Button("Detach Webview") 165 .onClick(() => { 166 this.nodeController.detachWeb(); 167 this.nodeController.rebuild(); 168 }) 169 // NodeContainer is used to bind to the NodeController node. Calling rebuild() triggers makeNode(). 170 // The Page is bound to NodeController() through NodeContainer(). As a result, the dynamic component page is successfully displayed. 171 NodeContainer(this.nodeController) 172 .height("80%") 173 .width("80%") 174 } 175 .width('100%') 176 } 177 .height('100%') 178 } 179} 180 181``` 182