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