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