1# ComponentContent 2 3**ComponentContent** represents an entity encapsulation of component content, which can be created and transmitted outside of UI components. It allows you to encapsulate and decouple dialog box components. The underlying implementation of **ComponentContent** uses BuilderNode. For details, see [BuilderNode](js-apis-arkui-builderNode.md). 4 5> **NOTE** 6> 7> The initial APIs of this module are supported since API version 12. Newly added APIs will be marked with a superscript to indicate their earliest API version. 8> 9> **ComponentContent** is not available in DevEco Studio Previewer. 10 11 12## Modules to Import 13 14```ts 15import { ComponentContent } from '@kit.ArkUI'; 16``` 17 18## ComponentContent 19 20### constructor 21 22constructor(uiContext: UIContext, builder: WrappedBuilder\<[]>) 23 24A constructor used to create a **ComponentContent** object. 25 26**Atomic service API**: This API can be used in atomic services since API version 12. 27 28**System capability**: SystemCapability.ArkUI.ArkUI.Full 29 30**Parameters** 31 32| Name | Type | Mandatory| Description | 33| --------- | ----------------------------------------- | ---- | ---------------------------------- | 34| uiContext | [UIContext](./js-apis-arkui-UIContext.md) | Yes | UI context required for creating the node.| 35| builder | [WrappedBuilder\<[]>](../../ui/state-management/arkts-wrapBuilder.md) | Yes | **WrappedBuilder** object that encapsulates a builder function that has no parameters.| 36 37### constructor 38 39constructor(uiContext: UIContext, builder: WrappedBuilder\<[T]>, args: T) 40 41A constructor used to create a **ComponentContent** object. 42 43**Atomic service API**: This API can be used in atomic services since API version 12. 44 45**System capability**: SystemCapability.ArkUI.ArkUI.Full 46 47**Parameters** 48 49| Name | Type | Mandatory| Description | 50| --------- | ----------------------------------------- | ---- | ---------------------------------- | 51| uiContext | [UIContext](./js-apis-arkui-UIContext.md) | Yes | UI context required for creating the node.| 52| builder | [WrappedBuilder\<[T]>](../../ui/state-management/arkts-wrapBuilder.md) | Yes | **WrappedBuilder** object that encapsulates a builder function that has parameters.| 53| args | T | Yes | Parameters of the builder function encapsulated in the **WrappedBuilder** object.| 54 55### constructor 56 57 constructor(uiContext: UIContext, builder: WrappedBuilder\<[T]>, args: T, options: BuildOptions) 58 59A constructor used to create a **ComponentContent** object. 60 61**Atomic service API**: This API can be used in atomic services since API version 12. 62 63**System capability**: SystemCapability.ArkUI.ArkUI.Full 64 65**Parameters** 66 67| Name | Type | Mandatory| Description | 68| --------- | ----------------------------------------- | ---- | ---------------------------------- | 69| uiContext | [UIContext](./js-apis-arkui-UIContext.md) | Yes | UI context required for creating the node.| 70| builder | [WrappedBuilder\<[T]>](../../ui/state-management/arkts-wrapBuilder.md) | Yes | **WrappedBuilder** object that encapsulates a builder function that has parameters.| 71| args | T | Yes | Parameters of the builder function encapsulated in the **WrappedBuilder** object.| 72| options | [BuildOptions](./js-apis-arkui-builderNode.md#buildoptions12) | Yes | Build options, which determine whether to support the behavior of nesting **@Builder** within **@Builder**. | 73 74**Example** 75``` ts 76import { ComponentContent, NodeContent, typeNode } from "@kit.ArkUI" 77 78interface ParamsInterface { 79 text: string; 80 func: Function; 81} 82 83@Builder 84function buildTextWithFunc(fun: Function) { 85 Text(fun()) 86 .fontSize(50) 87 .fontWeight(FontWeight.Bold) 88 .margin({ bottom: 36 }) 89} 90 91@Builder 92function buildText(params: ParamsInterface) { 93 Column() { 94 Text(params.text) 95 .fontSize(50) 96 .fontWeight(FontWeight.Bold) 97 .margin({ bottom: 36 }) 98 buildTextWithFunc(params.func) 99 } 100} 101 102@Entry 103@Component 104struct Index { 105 @State message: string = "HELLO" 106 private content: NodeContent = new NodeContent(); 107 108 build() { 109 Row() { 110 Column() { 111 Button('addComponentContent') 112 .onClick(() => { 113 let column = typeNode.createNode(this.getUIContext(), "Column"); 114 column.initialize(); 115 column.addComponentContent(new ComponentContent<ParamsInterface>(this.getUIContext(), 116 wrapBuilder<[ParamsInterface]>(buildText), { 117 text: this.message, func: () => { 118 return "FUNCTION" 119 } 120 }, { nestingBuilderSupported: true })) 121 this.content.addFrameNode(column); 122 }) 123 ContentSlot(this.content) 124 } 125 .id("column") 126 .width('100%') 127 .height('100%') 128 } 129 .height('100%') 130 } 131} 132 133``` 134 135### update 136 137update(args: T): void 138 139Updates the parameters of the builder function encapsulated in the **WrappedBuilder** object. The parameter type must be the same as that passed in **constructor**. 140 141**Atomic service API**: This API can be used in atomic services since API version 12. 142 143**System capability**: SystemCapability.ArkUI.ArkUI.Full 144 145**Parameters** 146 147| Name| Type| Mandatory| Description | 148| ------ | ---- | ---- | ------------------------------------------------------------ | 149| args | T | Yes | Parameters of the builder function encapsulated in the **WrappedBuilder** object. The parameter type must be the same as that passed in **constructor**.| 150 151**Example** 152 153```ts 154import { ComponentContent } from "@kit.ArkUI"; 155 156class Params { 157 text: string = "" 158 constructor(text: string) { 159 this.text = text; 160 } 161} 162 163@Builder 164function buildText(params: Params) { 165 Column() { 166 Text(params.text) 167 .fontSize(50) 168 .fontWeight(FontWeight.Bold) 169 .margin({bottom: 36}) 170 }.backgroundColor('#FFF0F0F0') 171} 172 173@Entry 174@Component 175struct Index { 176 @State message: string = "hello" 177 178 build() { 179 Row() { 180 Column() { 181 Button("click me") 182 .onClick(() => { 183 let uiContext = this.getUIContext(); 184 let promptAction = uiContext.getPromptAction(); 185 let contentNode = new ComponentContent(uiContext, wrapBuilder(buildText), new Params(this.message)); 186 promptAction.openCustomDialog(contentNode); 187 188 setTimeout(() => { 189 contentNode.update(new Params("new message")); 190 }, 2000); // Automatically update the text in the dialog box after 2 seconds. 191 }) 192 } 193 .width('100%') 194 .height('100%') 195 } 196 .height('100%') 197 } 198} 199``` 200 201### reuse 202 203reuse(param?: Object): void 204 205Passes the reuse event to the custom component in this **ComponentContent** object. 206 207**Atomic service API**: This API can be used in atomic services since API version 12. 208 209**System capability**: SystemCapability.ArkUI.ArkUI.Full 210 211**Parameters** 212 213| Name| Type | Mandatory| Description | 214| ------ | ------ | ---- | ------------------------------------------------------------------------ | 215| param | Object | No | Parameters of the builder function encapsulated in the **WrappedBuilder** object. The parameter type must be the same as that passed in **constructor**.| 216 217### recycle 218 219recycle(): void 220 221Passes the recycle event to the custom component in this **ComponentContent** object. 222 223**Atomic service API**: This API can be used in atomic services since API version 12. 224 225**System capability**: SystemCapability.ArkUI.ArkUI.Full 226 227```ts 228import { NodeContent, typeNode, ComponentContent } from "@kit.ArkUI"; 229 230const TEST_TAG: string = "Reuse+Recycle"; 231 232class MyDataSource { 233 private dataArray: string[] = []; 234 private listener: DataChangeListener | null = null 235 236 public totalCount(): number { 237 return this.dataArray.length; 238 } 239 240 public getData(index: number) { 241 return this.dataArray[index]; 242 } 243 244 public pushData(data: string) { 245 this.dataArray.push(data); 246 } 247 248 public reloadListener(): void { 249 this.listener?.onDataReloaded(); 250 } 251 252 public registerDataChangeListener(listener: DataChangeListener): void { 253 this.listener = listener; 254 } 255 256 public unregisterDataChangeListener(): void { 257 this.listener = null; 258 } 259} 260 261class Params { 262 item: string = ''; 263 264 constructor(item: string) { 265 this.item = item; 266 } 267} 268 269@Builder 270function buildNode(param: Params = new Params("hello")) { 271 Row() { 272 Text(`C${param.item} -- `) 273 ReusableChildComponent2({ item: param.item }) // This custom component cannot be correctly reused in the ComponentContent. 274 } 275} 276 277// The custom component that is reused and recycled will have its state variables updated, and the state variables of the nested custom component ReusableChildComponent3 will also be updated. However, the ComponentContent will block this propagation process. 278@Reusable 279@Component 280struct ReusableChildComponent { 281 @Prop item: string = ''; 282 @Prop switch: string = ''; 283 private content: NodeContent = new NodeContent(); 284 private componentContent: ComponentContent<Params> = new ComponentContent<Params>( 285 this.getUIContext(), 286 wrapBuilder<[Params]>(buildNode), 287 new Params(this.item), 288 { nestingBuilderSupported: true }); 289 290 aboutToAppear() { 291 let column = typeNode.createNode(this.getUIContext(), "Column"); 292 column.initialize(); 293 column.addComponentContent(this.componentContent); 294 this.content.addFrameNode(column); 295 } 296 297 aboutToRecycle(): void { 298 console.log(`${TEST_TAG} ReusableChildComponent aboutToRecycle ${this.item}`); 299 300 // When the switch is open, pass the recycle event to the nested custom component, such as ReusableChildComponent2, through the ComponentContent's recycle API to complete recycling. 301 if (this.switch === 'open') { 302 this.componentContent.recycle(); 303 } 304 } 305 306 aboutToReuse(params: object): void { 307 console.log(`${TEST_TAG} ReusableChildComponent aboutToReuse ${JSON.stringify(params)}`); 308 309 // When the switch is open, pass the reuse event to the nested custom component, such as ReusableChildComponent2, through the ComponentContent's reuse API to complete reuse. 310 if (this.switch === 'open') { 311 this.componentContent.reuse(params); 312 } 313 } 314 315 build() { 316 Row() { 317 Text(`A${this.item}--`) 318 ReusableChildComponent3({ item: this.item }) 319 ContentSlot(this.content) 320 } 321 } 322} 323 324@Component 325struct ReusableChildComponent2 { 326 @Prop item: string = "false"; 327 328 aboutToReuse(params: Record<string, object>) { 329 console.log(`${TEST_TAG} ReusableChildComponent2 aboutToReuse ${JSON.stringify(params)}`); 330 } 331 332 aboutToRecycle(): void { 333 console.log(`${TEST_TAG} ReusableChildComponent2 aboutToRecycle ${this.item}`); 334 } 335 336 build() { 337 Row() { 338 Text(`D${this.item}`) 339 .fontSize(20) 340 .backgroundColor(Color.Yellow) 341 .margin({ left: 10 }) 342 }.margin({ left: 10, right: 10 }) 343 } 344} 345 346@Component 347struct ReusableChildComponent3 { 348 @Prop item: string = "false"; 349 350 aboutToReuse(params: Record<string, object>) { 351 console.log(`${TEST_TAG} ReusableChildComponent3 aboutToReuse ${JSON.stringify(params)}`); 352 } 353 354 aboutToRecycle(): void { 355 console.log(`${TEST_TAG} ReusableChildComponent3 aboutToRecycle ${this.item}`); 356 } 357 358 build() { 359 Row() { 360 Text(`B${this.item}`) 361 .fontSize(20) 362 .backgroundColor(Color.Yellow) 363 .margin({ left: 10 }) 364 }.margin({ left: 10, right: 10 }) 365 } 366} 367 368 369@Entry 370@Component 371struct Index { 372 @State data: MyDataSource = new MyDataSource(); 373 374 aboutToAppear() { 375 for (let i = 0; i < 100; i++) { 376 this.data.pushData(i.toString()); 377 } 378 } 379 380 build() { 381 Column() { 382 List({ space: 3 }) { 383 LazyForEach(this.data, (item: string) => { 384 ListItem() { 385 ReusableChildComponent({ 386 item: item, 387 switch: 'open' // Changing open to close can be used to observe the behavior of custom components inside the ComponentContent when reuse and recycle events are not passed through the ComponentContent's reuse and recycle APIs. 388 }) 389 } 390 }, (item: string) => item) 391 } 392 .width('100%') 393 .height('100%') 394 } 395 } 396} 397``` 398 399### dispose 400 401dispose(): void 402 403Disposes of this **ComponentContent** object, which means to cancel the reference relationship between the **ComponentContent** object and its backend entity node. 404 405**Atomic service API**: This API can be used in atomic services since API version 12. 406 407**System capability**: SystemCapability.ArkUI.ArkUI.Full 408 409**Example** 410 411```ts 412import { BusinessError } from '@kit.BasicServicesKit'; 413import { ComponentContent } from '@kit.ArkUI'; 414 415class Params { 416 text: string = "" 417 constructor(text: string) { 418 this.text = text; 419 } 420} 421 422@Builder 423function buildText(params: Params) { 424 Column() { 425 Text(params.text) 426 .fontSize(50) 427 .fontWeight(FontWeight.Bold) 428 .margin({bottom: 36}) 429 }.backgroundColor('#FFF0F0F0') 430} 431 432@Entry 433@Component 434struct Index { 435 @State message: string = "hello" 436 437 build() { 438 Row() { 439 Column() { 440 Button("click me") 441 .onClick(() => { 442 let uiContext = this.getUIContext(); 443 let promptAction = uiContext.getPromptAction(); 444 let contentNode = new ComponentContent(uiContext, wrapBuilder(buildText), new Params(this.message)); 445 promptAction.openCustomDialog(contentNode); 446 447 setTimeout(() => { 448 promptAction.closeCustomDialog(contentNode) 449 .then(() => { 450 console.info('customdialog closed.') 451 if (contentNode !== null) { 452 contentNode.dispose(); // Dispose of the contentNode object. 453 } 454 }).catch((error: BusinessError) => { 455 let message = (error as BusinessError).message; 456 let code = (error as BusinessError).code; 457 console.error(`closeCustomDialog args error code is ${code}, message is ${message}`); 458 }) 459 }, 2000); // Automatically close the dialog box after 2 seconds. 460 }) 461 } 462 .width('100%') 463 .height('100%') 464 } 465 .height('100%') 466 } 467} 468``` 469 470 471### updateConfiguration 472 473updateConfiguration(): void 474 475Updates the configuration of the entire node by passing in a [system environment change](../apis-ability-kit/js-apis-app-ability-configuration.md) event. 476 477**Atomic service API**: This API can be used in atomic services since API version 12. 478 479**System capability**: SystemCapability.ArkUI.ArkUI.Full 480 481> **NOTE** 482> 483> The **updateConfiguration** API is used to instruct an object to update itself. The update is based on the current changes in the system environment. 484 485**Example** 486```ts 487import { NodeController, FrameNode, ComponentContent } from '@kit.ArkUI'; 488import { AbilityConstant, Configuration, EnvironmentCallback, ConfigurationConstant } from '@kit.AbilityKit'; 489 490@Builder 491function buildText() { 492 Column() { 493 Text('Hello') 494 .fontSize(36) 495 .fontWeight(FontWeight.Bold) 496 } 497 .backgroundColor($r('sys.color.ohos_id_color_background')) 498 .width('100%') 499 .alignItems(HorizontalAlign.Center) 500 .padding(16) 501} 502 503const componentContentMap: Array<ComponentContent<[Object]>> = new Array(); 504 505class MyNodeController extends NodeController { 506 private rootNode: FrameNode | null = null; 507 508 makeNode(uiContext: UIContext): FrameNode | null { 509 return this.rootNode; 510 } 511 512 createNode(context: UIContext) { 513 this.rootNode = new FrameNode(context); 514 let component = new ComponentContent<Object>(context, wrapBuilder(buildText)); 515 componentContentMap.push(component); 516 this.rootNode.addComponentContent(component); 517 } 518 519 deleteNode() { 520 let node = componentContentMap.pop(); 521 this.rootNode?.dispose(); 522 node?.dispose(); 523 } 524} 525 526function updateColorMode() { 527 componentContentMap.forEach((value, index) => { 528 value.updateConfiguration(); 529 }) 530} 531 532@Entry 533@Component 534struct FrameNodeTypeTest { 535 private myNodeController: MyNodeController = new MyNodeController(); 536 537 aboutToAppear(): void { 538 let environmentCallback: EnvironmentCallback = { 539 onMemoryLevel: (level: AbilityConstant.MemoryLevel): void => { 540 console.log('onMemoryLevel'); 541 }, 542 onConfigurationUpdated: (config: Configuration): void => { 543 console.log('onConfigurationUpdated ' + JSON.stringify(config)); 544 updateColorMode(); 545 } 546 } 547 // Register a callback. 548 this.getUIContext().getHostContext()?.getApplicationContext().on('environment', environmentCallback); 549 // Set the application color mode to follow the system settings. 550 this.getUIContext() 551 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_NOT_SET); 552 this.myNodeController.createNode(this.getUIContext()); 553 } 554 555 aboutToDisappear(): void { 556 // Remove the reference to the custom node from the map and release the node. 557 this.myNodeController.deleteNode(); 558 } 559 560 build() { 561 Column({ space: 16 }) { 562 NodeContainer(this.myNodeController); 563 Button('Switch to Dark Mode') 564 .onClick(() => { 565 this.getUIContext() 566 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_DARK); 567 }) 568 Button('Switch to Light Mode') 569 .onClick(() => { 570 this.getUIContext() 571 .getHostContext()?.getApplicationContext().setColorMode(ConfigurationConstant.ColorMode.COLOR_MODE_LIGHT); 572 }) 573 } 574 } 575} 576``` 577