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> ![icon-note.gif](../public_sys-resources/icon-note.gif) **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