• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Global Custom Dialog Box Independent of UI Components (openCustomDialog) (Recommended)
2
3Due to the restrictions of [CustomDialogController](../reference/apis-arkui/arkui-ts/ts-methods-custom-dialog-box.md#customdialogcontroller), such as lack of support for dynamic creation and refresh, for more complex use cases, you are advised to use the [openCustomDialog](../reference/apis-arkui/js-apis-arkui-UIContext.md#opencustomdialog12) API provided by the **PromptAction** object obtained from **UIContext** to implement a custom dialog box.
4
5> **NOTE**
6>
7> There are two ways to create a custom dialog using **openCustomDialog**:
8> - **openCustomDialog** with **ComponentContent**: By encapsulating content using **ComponentContent**, the dialog box can be decoupled from the UI, offering greater flexibility and satisfying needs for encapsulation. This approach provides full customizability of the dialog box style and allows for dynamic updates to dialog box parameters using the **updateCustomDialog** API after the dialog box is opened.
9> - **openCustomDialog** with **builder**: Unlike **ComponentContent**, the builder must be bound to the context and is somewhat coupled with the UI. This approach provides a default dialog box style, suitable for those who want to achieve a look consistent with the system's default dialog box style.
10>
11> This topic focuses on creating custom dialog boxes using **ComponentContent**. For the usage of the builder-based dialog box, see [openCustomDialog](../reference/apis-arkui/js-apis-arkui-UIContext.md#opencustomdialog12-1).
12
13**openCustomDialog** can be configured with [isModal](../reference/apis-arkui/js-apis-arkui-UIContext.md#opencustomdialog12) to create modal and non-modal dialog boxes. When **isModal** is set to **true**, the dialog box is modal. When **isModal** is set to **false**, the dialog box is non-modal.
14
15## Lifecycle
16
17The dialog box provides lifecycle functions to notify users of its lifecycle events. The order in which these lifecycle events are triggered is as follows: **onWillAppear**, **onDidAppear**, **onWillDisappear**, **onDidDisappear**.
18
19| Name           |Type| Description                      |
20| ----------------- | ------ | ---------------------------- |
21| onDidAppear    | () => void  | Callback invoked when the dialog box appears.   |
22| onDidDisappear |() => void  | Callback invoked when the dialog box disappears.   |
23| onWillAppear    | () => void | Callback invoked when the dialog box is about to appear.|
24| onWillDisappear | () => void | Callback invoked when the dialog box is about to disappear.|
25
26## Opening and Closing a Custom Dialog Box
27
28> **NOTE**
29>
30> For details about the variables, see [Example](#example).
31
321. Create a **ComponentContent** instance.
33
34   **ComponentContent** is used to define the content of the custom dialog box. **wrapBuilder(buildText)** encapsulates the custom component, and **new Params(this.message)** is the input parameter for the custom component, which can be omitted or passed in with basic data types.
35
36   ```ts
37   private contentNode: ComponentContent<Object> = new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));
38   ```
392. Open the custom dialog box.
40
41   Call **openCustomDialog** to open the custom dialog box, whose **customStyle** is set to **true** by default, meaning that the dialog box is styled entirely based on the **contentNode** settings you provide.
42
43   ```ts
44   PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
45     .then(() => {
46       console.info('OpenCustomDialog complete.')
47     })
48     .catch((error: BusinessError) => {
49       let message = (error as BusinessError).message;
50       let code = (error as BusinessError).code;
51       console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
52     })
53   ```
543. Close the custom dialog box.
55
56   Call **closeCustomDialog**, which requires the ComponentContent corresponding to the dialog box to be closed. To set a close method within the dialog box, follow the complete sample to encapsulate this functionality into a static method.
57
58   To release the corresponding **ComponentContent** after the dialog box is closed, call the [dispose](../reference/apis-arkui/js-apis-arkui-ComponentContent.md#dispose) API of the **ComponentContent**.
59
60   ```ts
61
62   PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
63     .then(() => {
64       console.info('CloseCustomDialog complete.')
65       if (this.contentNode !== null) {
66            this.contentNode.dispose();   // Dispose of contentNode.
67        }
68     })
69     .catch((error: BusinessError) => {
70       let message = (error as BusinessError).message;
71       let code = (error as BusinessError).code;
72       console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
73     })
74   ```
75
76## Updating the Content of a Custom Dialog Box
77
78**ComponentContent** has the same usage constraints as [BuilderNode](../reference/apis-arkui/js-apis-arkui-builderNode.md) and does not support custom components using decorators such as [@Reusable](../ui/state-management/arkts-create-custom-components.md#basic-structure-of-a-custom-component), [@Link](../ui/state-management/arkts-link.md), [@Provide](../ui/state-management/arkts-provide-and-consume.md), and [@Consume](../ui/state-management/arkts-provide-and-consume.md) to synchronize the state between the page where the dialog box pops up and the custom component in **ComponentContent**. Therefore, if you need to update the content of the custom component in the dialog box, use the **update** API provided by **ComponentContent**.
79
80```ts
81this.contentNode.update(new Params('update'))
82```
83
84## Updating the Attributes of a Custom Dialog Box
85
86You can dynamically update the attributes of the dialog box through **updateCustomDialog**. Currently, the following attributes are supported: **alignment**, **offset**, **autoCancel**, and **maskColor**.
87Note that when attributes are updated, those unset will be restored to their default values. For example, if you initially set **{ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } }** and then update it to **{ alignment: DialogAlignment.Bottom }**, the initially set **offset: { dx: 0, dy: 50 }** will not be retained; the offset will be restored to the default value.
88
89```ts
90PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
91  .then(() => {
92    console.info('UpdateCustomDialog complete.')
93  })
94  .catch((error: BusinessError) => {
95    let message = (error as BusinessError).message;
96    let code = (error as BusinessError).code;
97    console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
98  })
99```
100
101## Example
102
103```ts
104// PromptActionClass.ets
105import { BusinessError } from '@kit.BasicServicesKit';
106import { ComponentContent, promptAction } from '@kit.ArkUI';
107import { UIContext } from '@ohos.arkui.UIContext';
108
109export class PromptActionClass {
110  static ctx: UIContext;
111  static contentNode: ComponentContent<Object>;
112  static options: promptAction.BaseDialogOptions;
113
114  static setContext(context: UIContext) {
115    PromptActionClass.ctx = context;
116  }
117
118  static setContentNode(node: ComponentContent<Object>) {
119    PromptActionClass.contentNode = node;
120  }
121
122  static setOptions(options: promptAction.BaseDialogOptions) {
123    PromptActionClass.options = options;
124  }
125
126  static openDialog() {
127    if (PromptActionClass.contentNode !== null) {
128      PromptActionClass.ctx.getPromptAction().openCustomDialog(PromptActionClass.contentNode, PromptActionClass.options)
129        .then(() => {
130          console.info('OpenCustomDialog complete.')
131        })
132        .catch((error: BusinessError) => {
133          let message = (error as BusinessError).message;
134          let code = (error as BusinessError).code;
135          console.error(`OpenCustomDialog args error code is ${code}, message is ${message}`);
136        })
137    }
138  }
139
140  static closeDialog() {
141    if (PromptActionClass.contentNode !== null) {
142      PromptActionClass.ctx.getPromptAction().closeCustomDialog(PromptActionClass.contentNode)
143        .then(() => {
144          console.info('CloseCustomDialog complete.')
145        })
146        .catch((error: BusinessError) => {
147          let message = (error as BusinessError).message;
148          let code = (error as BusinessError).code;
149          console.error(`CloseCustomDialog args error code is ${code}, message is ${message}`);
150        })
151    }
152  }
153
154  static updateDialog(options: promptAction.BaseDialogOptions) {
155    if (PromptActionClass.contentNode !== null) {
156      PromptActionClass.ctx.getPromptAction().updateCustomDialog(PromptActionClass.contentNode, options)
157        .then(() => {
158          console.info('UpdateCustomDialog complete.')
159        })
160        .catch((error: BusinessError) => {
161          let message = (error as BusinessError).message;
162          let code = (error as BusinessError).code;
163          console.error(`UpdateCustomDialog args error code is ${code}, message is ${message}`);
164        })
165    }
166  }
167}
168```
169
170```ts
171// Index.ets
172import { ComponentContent } from '@kit.ArkUI';
173import { PromptActionClass } from './PromptActionClass';
174
175class Params {
176  text: string = ""
177
178  constructor(text: string) {
179    this.text = text;
180  }
181}
182
183@Builder
184function buildText(params: Params) {
185  Column() {
186    Text(params.text)
187      .fontSize(50)
188      .fontWeight(FontWeight.Bold)
189      .margin({ bottom: 36 })
190    Button('Close')
191      .onClick(() => {
192        PromptActionClass.closeDialog()
193      })
194  }.backgroundColor('#FFF0F0F0')
195}
196
197@Entry
198@Component
199struct Index {
200  @State message: string = "hello"
201  private ctx: UIContext = this.getUIContext();
202  private contentNode: ComponentContent<Object> =
203    new ComponentContent(this.ctx, wrapBuilder(buildText), new Params(this.message));
204
205  aboutToAppear(): void {
206    PromptActionClass.setContext(this.ctx);
207    PromptActionClass.setContentNode(this.contentNode);
208    PromptActionClass.setOptions({ alignment: DialogAlignment.Top, offset: { dx: 0, dy: 50 } });
209  }
210
211  build() {
212    Row() {
213      Column() {
214        Button("open dialog and update options")
215          .margin({ top: 50 })
216          .onClick(() => {
217            PromptActionClass.openDialog()
218
219            setTimeout(() => {
220              PromptActionClass.updateDialog({
221                alignment: DialogAlignment.Bottom,
222                offset: { dx: 0, dy: -50 }
223              })
224            }, 1500)
225          })
226        Button("open dialog and update content")
227          .margin({ top: 50 })
228          .onClick(() => {
229            PromptActionClass.openDialog()
230
231            setTimeout(() => {
232              this.contentNode.update(new Params('update'))
233            }, 1500)
234          })
235      }
236      .width('100%')
237      .height('100%')
238    }
239    .height('100%')
240  }
241}
242```
243
244 ![UIContextPromptAction](figures/UIContextPromptAction.gif)
245