1e41f4b71Sopenharmony_ci# Developing a JS Widget
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci
4e41f4b71Sopenharmony_ciThe following describes how to develop JS widgets based on the web-like development paradigm.
5e41f4b71Sopenharmony_ci
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ci## Working Principles
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ciBelow shows the working principles of the widget framework.
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ci**Figure 1** Widget framework working principles in the stage model
12e41f4b71Sopenharmony_ci
13e41f4b71Sopenharmony_ci![JSCardPrinciple](figures/JSCardPrinciple.png)
14e41f4b71Sopenharmony_ci
15e41f4b71Sopenharmony_ciThe widget host consists of the following modules:
16e41f4b71Sopenharmony_ci
17e41f4b71Sopenharmony_ci- Widget usage: provides operations such as creating, deleting, or updating a widget.
18e41f4b71Sopenharmony_ci
19e41f4b71Sopenharmony_ci- Communication adapter: provided by the SDK for communication with the Widget Manager. It sends widget-related operations to the Widget Manager.
20e41f4b71Sopenharmony_ci
21e41f4b71Sopenharmony_ciThe Widget Manager consists of the following modules:
22e41f4b71Sopenharmony_ci
23e41f4b71Sopenharmony_ci- Periodic updater: starts a scheduled task based on the update policy to periodically update a widget after it is added to the Widget Manager.
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ci- Cache manager: caches view information of a widget after it is added to the Widget Manager. This enables the cached data to be directly returned when the widget is obtained next time, greatly reducing the latency.
26e41f4b71Sopenharmony_ci
27e41f4b71Sopenharmony_ci- Lifecycle manager: suspends update when a widget is switched to the background or is blocked, and updates and/or clears widget data during upgrade and deletion.
28e41f4b71Sopenharmony_ci
29e41f4b71Sopenharmony_ci- Object manager: manages RPC objects of the widget host. It is used to verify requests from the widget host and process callbacks after the widget update.
30e41f4b71Sopenharmony_ci
31e41f4b71Sopenharmony_ci- Communication adapter: communicates with the widget host and provider through RPCs.
32e41f4b71Sopenharmony_ci
33e41f4b71Sopenharmony_ciThe widget provider consists of the following modules:
34e41f4b71Sopenharmony_ci
35e41f4b71Sopenharmony_ci- Widget service: implemented by the widget provider developer to process requests on widget creation, update, and deletion, and to provide corresponding widget services.
36e41f4b71Sopenharmony_ci
37e41f4b71Sopenharmony_ci- Instance manager: implemented by the widget provider developer for persistent management of widget instances allocated by the Widget Manager.
38e41f4b71Sopenharmony_ci
39e41f4b71Sopenharmony_ci- Communication adapter: provided by the SDK for communication with the Widget Manager. It pushes update data to the Widget Manager.
40e41f4b71Sopenharmony_ci
41e41f4b71Sopenharmony_ci> **NOTE**
42e41f4b71Sopenharmony_ci> 
43e41f4b71Sopenharmony_ci> You only need to develop the widget provider. The system automatically handles the work of the widget host and Widget Manager.
44e41f4b71Sopenharmony_ci
45e41f4b71Sopenharmony_ci
46e41f4b71Sopenharmony_ci## Available APIs
47e41f4b71Sopenharmony_ci
48e41f4b71Sopenharmony_ciThe **FormExtensionAbility** class has the following APIs. For details, see [FormExtensionAbility](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md).
49e41f4b71Sopenharmony_ci
50e41f4b71Sopenharmony_ci| Name                                                                                             | Description |
51e41f4b71Sopenharmony_ci| -------- | -------- |
52e41f4b71Sopenharmony_ci| onAddForm(want: Want): formBindingData.FormBindingData                                 | Called to notify the widget provider that a widget is being created. |
53e41f4b71Sopenharmony_ci| onCastToNormalForm(formId: string): void                                               | Called to notify the widget provider that a temporary widget is being converted to a normal one. |
54e41f4b71Sopenharmony_ci| onUpdateForm(formId: string): void                                                     | Called to notify the widget provider that a widget is being updated. |
55e41f4b71Sopenharmony_ci| onChangeFormVisibility(newStatus: Record<string, number>): void             | Called to notify the widget provider that the widget visibility status is being changed. |
56e41f4b71Sopenharmony_ci| onFormEvent(formId: string, message: string): void                           | Called to instruct the widget provider to process a widget event. |
57e41f4b71Sopenharmony_ci| onRemoveForm(formId: string): void                                                     | Called to notify the widget provider that a widget is being destroyed. |
58e41f4b71Sopenharmony_ci| onConfigurationUpdate(newConfig: Configuration): void                                  | Called when the configuration of the environment where the widget is running is being updated. |
59e41f4b71Sopenharmony_ci| onShareForm?(formId: string): Record<string, Object>                        | Called to notify the widget provider that the widget host is sharing the widget data. |
60e41f4b71Sopenharmony_ci
61e41f4b71Sopenharmony_ciThe **FormProvider** class has the following APIs. For details, see [FormProvider](../reference/apis-form-kit/js-apis-app-form-formProvider.md).
62e41f4b71Sopenharmony_ci
63e41f4b71Sopenharmony_ci| Name | Description |
64e41f4b71Sopenharmony_ci| -------- | -------- |
65e41f4b71Sopenharmony_ci| setFormNextRefreshTime(formId: string, minute: number, callback: AsyncCallback<void>): void | Sets the next refresh time for a widget. This API uses an asynchronous callback to return the result. |
66e41f4b71Sopenharmony_ci| setFormNextRefreshTime(formId: string, minute: number): Promise<void> | Sets the next refresh time for a widget. This API uses a promise to return the result. |
67e41f4b71Sopenharmony_ci| updateForm(formId: string, formBindingData: formBindingData.FormBindingData, callback: AsyncCallback<void>): void | Updates a widget. This API uses an asynchronous callback to return the result. |
68e41f4b71Sopenharmony_ci| updateForm(formId: string, formBindingData: formBindingData.FormBindingData): Promise<void> | Updates a widget. This API uses a promise to return the result. |
69e41f4b71Sopenharmony_ci
70e41f4b71Sopenharmony_ciThe **FormBindingData** class has the following APIs. For details, see [FormBindingData](../reference/apis-form-kit/js-apis-app-form-formBindingData.md).
71e41f4b71Sopenharmony_ci
72e41f4b71Sopenharmony_ci| Name | Description |
73e41f4b71Sopenharmony_ci| -------- | -------- |
74e41f4b71Sopenharmony_ci| createFormBindingData(obj?: Object \| string): FormBindingData | Creates a **FormBindingData** object. |
75e41f4b71Sopenharmony_ci
76e41f4b71Sopenharmony_ci
77e41f4b71Sopenharmony_ci## How to Develop
78e41f4b71Sopenharmony_ci
79e41f4b71Sopenharmony_ciThe widget provider development based on the [stage model](../application-models/stage-model-development-overview.md) involves the following key steps:
80e41f4b71Sopenharmony_ci
81e41f4b71Sopenharmony_ci- [Creating a FormExtensionAbility Instance](#creating-a-formextensionability-instance): Develop the lifecycle callback functions of FormExtensionAbility.
82e41f4b71Sopenharmony_ci
83e41f4b71Sopenharmony_ci- [Configuring the Widget Configuration Files](#configuring-the-widget-configuration-files): Configure the application configuration file **module.json5** and profile configuration file.
84e41f4b71Sopenharmony_ci
85e41f4b71Sopenharmony_ci- [Persistently Storing Widget Data](#persistently-storing-widget-data): Manage widget data persistence.
86e41f4b71Sopenharmony_ci
87e41f4b71Sopenharmony_ci- [Updating Widget Data](#updating-widget-data): Call **updateForm()** to update the information displayed in a widget.
88e41f4b71Sopenharmony_ci
89e41f4b71Sopenharmony_ci- [Developing the Widget UI Page](#developing-the-widget-ui-page): Use HML+CSS+JSON to develop a JS widget UI page.
90e41f4b71Sopenharmony_ci
91e41f4b71Sopenharmony_ci- [Developing Widget Events](#developing-widget-events): Add the router and message events for a widget.
92e41f4b71Sopenharmony_ci
93e41f4b71Sopenharmony_ci
94e41f4b71Sopenharmony_ci### Creating a FormExtensionAbility Instance
95e41f4b71Sopenharmony_ci
96e41f4b71Sopenharmony_ciTo create a widget in the stage model, you need to implement the lifecycle callbacks of FormExtensionAbility. Create a widget template in DevEco Studio<!--RP1--> by referring to [Creating a Service Widget](https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V2/ide_service_widget-0000001078566997-V2)<!--RP1End--> and then perform the following:
97e41f4b71Sopenharmony_ci
98e41f4b71Sopenharmony_ci1. Import related modules to **EntryFormAbility.ets**.
99e41f4b71Sopenharmony_ci    ```ts
100e41f4b71Sopenharmony_ci    import { Want } from '@kit.AbilityKit';
101e41f4b71Sopenharmony_ci    import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
102e41f4b71Sopenharmony_ci    import { hilog } from '@kit.PerformanceAnalysisKit';
103e41f4b71Sopenharmony_ci    import { BusinessError } from '@kit.BasicServicesKit';
104e41f4b71Sopenharmony_ci    
105e41f4b71Sopenharmony_ci    const TAG: string = 'JsCardFormAbility';
106e41f4b71Sopenharmony_ci    const DOMAIN_NUMBER: number = 0xFF00;
107e41f4b71Sopenharmony_ci    ```
108e41f4b71Sopenharmony_ci
109e41f4b71Sopenharmony_ci2. Implement the FormExtension lifecycle callbacks in **EntryFormAbility.ets**.
110e41f4b71Sopenharmony_ci
111e41f4b71Sopenharmony_ci    ```ts
112e41f4b71Sopenharmony_ci    export default class EntryFormAbility extends FormExtensionAbility {
113e41f4b71Sopenharmony_ci      onAddForm(want: Want): formBindingData.FormBindingData {
114e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onAddForm');
115e41f4b71Sopenharmony_ci        // Called when the widget is created. The widget provider should return the widget data binding class.
116e41f4b71Sopenharmony_ci        let obj: Record<string, string> = {
117e41f4b71Sopenharmony_ci          'title': 'titleOnCreate',
118e41f4b71Sopenharmony_ci          'detail': 'detailOnCreate'
119e41f4b71Sopenharmony_ci        };
120e41f4b71Sopenharmony_ci        let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
121e41f4b71Sopenharmony_ci        return formData;
122e41f4b71Sopenharmony_ci      }
123e41f4b71Sopenharmony_ci      onCastToNormalForm(formId: string): void {
124e41f4b71Sopenharmony_ci        // Called when a temporary widget is being converted into a normal one. The widget provider should respond to the conversion.
125e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onCastToNormalForm');
126e41f4b71Sopenharmony_ci      }
127e41f4b71Sopenharmony_ci      onUpdateForm(formId: string): void {
128e41f4b71Sopenharmony_ci        // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host.
129e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onUpdateForm');
130e41f4b71Sopenharmony_ci        let obj: Record<string, string> = {
131e41f4b71Sopenharmony_ci          'title': 'titleOnUpdate',
132e41f4b71Sopenharmony_ci          'detail': 'detailOnUpdate'
133e41f4b71Sopenharmony_ci        };
134e41f4b71Sopenharmony_ci        let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
135e41f4b71Sopenharmony_ci        formProvider.updateForm(formId, formData).catch((error: BusinessError) => {
136e41f4b71Sopenharmony_ci          hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
137e41f4b71Sopenharmony_ci        });
138e41f4b71Sopenharmony_ci      }
139e41f4b71Sopenharmony_ci      onChangeFormVisibility(newStatus: Record<string, number>): void {
140e41f4b71Sopenharmony_ci        // Called when the widget host initiates an event about visibility changes. The widget provider should do something to respond to the notification. This callback takes effect only for system applications.
141e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onChangeFormVisibility');
142e41f4b71Sopenharmony_ci        //...
143e41f4b71Sopenharmony_ci      }
144e41f4b71Sopenharmony_ci      onFormEvent(formId: string, message: string): void {
145e41f4b71Sopenharmony_ci        // If the widget supports event triggering, override this method and implement the trigger.
146e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent');
147e41f4b71Sopenharmony_ci      }
148e41f4b71Sopenharmony_ci      onRemoveForm(formId: string): void {
149e41f4b71Sopenharmony_ci        // Delete widget data.
150e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onRemoveForm');
151e41f4b71Sopenharmony_ci        //...
152e41f4b71Sopenharmony_ci      }
153e41f4b71Sopenharmony_ci      onAcquireFormState(want: Want): formInfo.FormState {
154e41f4b71Sopenharmony_ci        return formInfo.FormState.READY;
155e41f4b71Sopenharmony_ci      }
156e41f4b71Sopenharmony_ci    }
157e41f4b71Sopenharmony_ci    ```
158e41f4b71Sopenharmony_ci
159e41f4b71Sopenharmony_ci> **NOTE**
160e41f4b71Sopenharmony_ci>
161e41f4b71Sopenharmony_ci> FormExtensionAbility cannot reside in the background. Therefore, continuous tasks cannot be processed in the widget lifecycle callbacks.
162e41f4b71Sopenharmony_ci
163e41f4b71Sopenharmony_ci
164e41f4b71Sopenharmony_ci### Configuring the Widget Configuration Files
165e41f4b71Sopenharmony_ci
166e41f4b71Sopenharmony_ci1. Configure ExtensionAbility information under **extensionAbilities** in the [module.json5 file](../quick-start/module-configuration-file.md). For a FormExtensionAbility, you must specify **metadata**. Specifically, set **name** to **ohos.extension.form** (fixed), and set **resource** to the index of the widget configuration information.
167e41f4b71Sopenharmony_ci   Example configuration:
168e41f4b71Sopenharmony_ci
169e41f4b71Sopenharmony_ci   
170e41f4b71Sopenharmony_ci   ```json
171e41f4b71Sopenharmony_ci   {
172e41f4b71Sopenharmony_ci     "module": {
173e41f4b71Sopenharmony_ci       ...
174e41f4b71Sopenharmony_ci       "extensionAbilities": [
175e41f4b71Sopenharmony_ci         {
176e41f4b71Sopenharmony_ci           "name": "JsCardFormAbility",
177e41f4b71Sopenharmony_ci           "srcEntry": "./ets/jscardformability/JsCardFormAbility.ts",
178e41f4b71Sopenharmony_ci           "description": "$string:JSCardFormAbility_desc",
179e41f4b71Sopenharmony_ci           "label": "$string:JSCardFormAbility_label",
180e41f4b71Sopenharmony_ci           "type": "form",
181e41f4b71Sopenharmony_ci           "metadata": [
182e41f4b71Sopenharmony_ci             {
183e41f4b71Sopenharmony_ci               "name": "ohos.extension.form",
184e41f4b71Sopenharmony_ci               "resource": "$profile:form_jscard_config"
185e41f4b71Sopenharmony_ci             }
186e41f4b71Sopenharmony_ci           ]
187e41f4b71Sopenharmony_ci         }
188e41f4b71Sopenharmony_ci       ]
189e41f4b71Sopenharmony_ci     }
190e41f4b71Sopenharmony_ci   }
191e41f4b71Sopenharmony_ci   ```
192e41f4b71Sopenharmony_ci
193e41f4b71Sopenharmony_ci2. Configure the widget configuration information. In the **metadata** configuration item of FormExtensionAbility, you can specify the resource index of specific configuration information of the widget. For example, if **resource** is set to **$profile:form_config**, **form_config.json** in the **resources/base/profile/** directory of the development view is used as the profile configuration file of the widget. The following table describes the internal structure of the profile configuration file.
194e41f4b71Sopenharmony_ci
195e41f4b71Sopenharmony_ci   **Table 1** Widget profile configuration file
196e41f4b71Sopenharmony_ci
197e41f4b71Sopenharmony_ci   | Field | Description | Data Type | Default Value Allowed |
198e41f4b71Sopenharmony_ci   | -------- | -------- | -------- | -------- |
199e41f4b71Sopenharmony_ci   | name | Class name of the widget. The value is a string with a maximum of 127 bytes. | String | No |
200e41f4b71Sopenharmony_ci   | description | Description of the widget. The value can be a string or a resource index to descriptions in multiple languages. The value is a string with a maximum of 255 bytes. | String | Yes (initial value: left empty) |
201e41f4b71Sopenharmony_ci   | src | Full path of the UI code corresponding to the widget. | String | No |
202e41f4b71Sopenharmony_ci   | window | Window-related configurations. | Object | Yes |
203e41f4b71Sopenharmony_ci   | isDefault | Whether the widget is a default one. Each UIAbility has only one default widget.<br>- **true**: The widget is the default one.<br>- **false**: The widget is not the default one. | Boolean | No |
204e41f4b71Sopenharmony_ci   | colorMode | Color mode of the widget.<br>- **auto**: auto-adaptive color mode<br>- **dark**: dark color mode<br>- **light**: light color mode | String | Yes (initial value: **auto**) |
205e41f4b71Sopenharmony_ci   | supportDimensions | Grid styles supported by the widget.<br>- **1 * 2**: indicates a grid with one row and two columns.<br>- **2 * 2**: indicates a grid with two rows and two columns.<br>- **2 * 4**: indicates a grid with two rows and four columns.<br>- **4 * 4**: indicates a grid with four rows and four columns. | String array | No |
206e41f4b71Sopenharmony_ci   | defaultDimension | Default grid style of the widget. The value must be available in the **supportDimensions** array of the widget. | String | No |
207e41f4b71Sopenharmony_ci   | updateEnabled | Whether the widget can be updated periodically.<br>- **true**: The widget can be updated at a specified interval (**updateDuration**) or at the scheduled time (**scheduledUpdateTime**). **updateDuration** takes precedence over **scheduledUpdateTime**.<br>- **false**: The widget cannot be updated periodically. | Boolean | No |
208e41f4b71Sopenharmony_ci   | scheduledUpdateTime | Scheduled time to update the widget. The value is in 24-hour format and accurate to minute.<br>**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used. | String | Yes (initial value: **0:0**) |
209e41f4b71Sopenharmony_ci   | updateDuration | Interval to update the widget. The value is a natural number, in the unit of 30 minutes.<br>If the value is **0**, this field does not take effect.<br>If the value is a positive integer *N*, the interval is calculated by multiplying *N* and 30 minutes.<br>**updateDuration** takes precedence over **scheduledUpdateTime**. If both are specified, the value specified by **updateDuration** is used. | Number | Yes (initial value: **0**) |
210e41f4b71Sopenharmony_ci   | formConfigAbility | Link to a specific page of the application. The value is a URI. | String | Yes (initial value: left empty) |
211e41f4b71Sopenharmony_ci   | formVisibleNotify | Whether the widget is allowed to use the widget visibility notification. | String | Yes (initial value: left empty) |
212e41f4b71Sopenharmony_ci   | metaData | Metadata of the widget. This field contains the array of the **customizeData** field. | Object | Yes (initial value: left empty) |
213e41f4b71Sopenharmony_ci
214e41f4b71Sopenharmony_ci   Example configuration:
215e41f4b71Sopenharmony_ci
216e41f4b71Sopenharmony_ci   
217e41f4b71Sopenharmony_ci   ```json
218e41f4b71Sopenharmony_ci   {
219e41f4b71Sopenharmony_ci     "forms": [
220e41f4b71Sopenharmony_ci       {
221e41f4b71Sopenharmony_ci         "name": "WidgetJS",
222e41f4b71Sopenharmony_ci         "description": "$string:JSCardEntryAbility_desc",
223e41f4b71Sopenharmony_ci         "src": "./js/WidgetJS/pages/index/index",
224e41f4b71Sopenharmony_ci         "window": {
225e41f4b71Sopenharmony_ci           "designWidth": 720,
226e41f4b71Sopenharmony_ci           "autoDesignWidth": true
227e41f4b71Sopenharmony_ci         },
228e41f4b71Sopenharmony_ci         "colorMode": "auto",
229e41f4b71Sopenharmony_ci         "isDefault": true,
230e41f4b71Sopenharmony_ci         "updateEnabled": true,
231e41f4b71Sopenharmony_ci         "scheduledUpdateTime": "10:30",
232e41f4b71Sopenharmony_ci         "updateDuration": 1,
233e41f4b71Sopenharmony_ci         "defaultDimension": "2*2",
234e41f4b71Sopenharmony_ci         "supportDimensions": [
235e41f4b71Sopenharmony_ci           "2*2"
236e41f4b71Sopenharmony_ci         ]
237e41f4b71Sopenharmony_ci       }
238e41f4b71Sopenharmony_ci     ]
239e41f4b71Sopenharmony_ci   }
240e41f4b71Sopenharmony_ci   ```
241e41f4b71Sopenharmony_ci
242e41f4b71Sopenharmony_ci
243e41f4b71Sopenharmony_ci### Persistently Storing Widget Data
244e41f4b71Sopenharmony_ci
245e41f4b71Sopenharmony_ciA widget provider is usually started when it is needed to provide information about a widget. The Widget Manager supports multi-instance management and uses the widget ID to identify an instance. If the widget provider supports widget data modification, it must persistently store the data based on the widget ID, so that it can access the data of the target widget when obtaining, updating, or starting a widget.
246e41f4b71Sopenharmony_ci
247e41f4b71Sopenharmony_ci
248e41f4b71Sopenharmony_ci```ts
249e41f4b71Sopenharmony_ciimport { common, Want } from '@kit.AbilityKit';
250e41f4b71Sopenharmony_ciimport { hilog } from '@kit.PerformanceAnalysisKit';
251e41f4b71Sopenharmony_ciimport { formBindingData, FormExtensionAbility } from '@kit.FormKit';
252e41f4b71Sopenharmony_ciimport { BusinessError } from '@kit.BasicServicesKit';
253e41f4b71Sopenharmony_ciimport { preferences } from '@kit.ArkData';
254e41f4b71Sopenharmony_ci
255e41f4b71Sopenharmony_ciconst TAG: string = 'JsCardFormAbility';
256e41f4b71Sopenharmony_ciconst DATA_STORAGE_PATH: string = '/data/storage/el2/base/haps/form_store';
257e41f4b71Sopenharmony_ciconst DOMAIN_NUMBER: number = 0xFF00;
258e41f4b71Sopenharmony_ci
259e41f4b71Sopenharmony_cilet storeFormInfo = async (formId: string, formName: string, tempFlag: boolean, context: common.FormExtensionContext): Promise<void> => {
260e41f4b71Sopenharmony_ci  // Only the widget ID (formId), widget name (formName), and whether the widget is a temporary one (tempFlag) are persistently stored.
261e41f4b71Sopenharmony_ci  let formInfo: Record<string, string | boolean | number> = {
262e41f4b71Sopenharmony_ci    'formName': formName,
263e41f4b71Sopenharmony_ci    'tempFlag': tempFlag,
264e41f4b71Sopenharmony_ci    'updateCount': 0
265e41f4b71Sopenharmony_ci  };
266e41f4b71Sopenharmony_ci  try {
267e41f4b71Sopenharmony_ci    const storage: preferences.Preferences = await preferences.getPreferences(context, DATA_STORAGE_PATH);
268e41f4b71Sopenharmony_ci    // put form info
269e41f4b71Sopenharmony_ci    await storage.put(formId, JSON.stringify(formInfo));
270e41f4b71Sopenharmony_ci    hilog.info(DOMAIN_NUMBER, TAG, `[EntryFormAbility] storeFormInfo, put form info successfully, formId: ${formId}`);
271e41f4b71Sopenharmony_ci    await storage.flush();
272e41f4b71Sopenharmony_ci  } catch (err) {
273e41f4b71Sopenharmony_ci    hilog.error(DOMAIN_NUMBER, TAG, `[EntryFormAbility] failed to storeFormInfo, err: ${JSON.stringify(err as BusinessError)}`);
274e41f4b71Sopenharmony_ci  }
275e41f4b71Sopenharmony_ci}
276e41f4b71Sopenharmony_ci
277e41f4b71Sopenharmony_ciexport default class JsCardFormAbility extends FormExtensionAbility {
278e41f4b71Sopenharmony_ci  onAddForm(want: Want): formBindingData.FormBindingData {
279e41f4b71Sopenharmony_ci    hilog.info(DOMAIN_NUMBER, TAG, '[JsCardFormAbility] onAddForm');
280e41f4b71Sopenharmony_ci
281e41f4b71Sopenharmony_ci    if (want.parameters) {
282e41f4b71Sopenharmony_ci      let formId = JSON.stringify(want.parameters['ohos.extra.param.key.form_identity']);
283e41f4b71Sopenharmony_ci      let formName = JSON.stringify(want.parameters['ohos.extra.param.key.form_name']);
284e41f4b71Sopenharmony_ci      let tempFlag = want.parameters['ohos.extra.param.key.form_temporary'] as boolean;
285e41f4b71Sopenharmony_ci      // Persistently store widget data for subsequent use, such as instance acquisition and update.
286e41f4b71Sopenharmony_ci      // Implement this API based on project requirements.
287e41f4b71Sopenharmony_ci      storeFormInfo(formId, formName, tempFlag, this.context);
288e41f4b71Sopenharmony_ci    }
289e41f4b71Sopenharmony_ci
290e41f4b71Sopenharmony_ci    let obj: Record<string, string> = {
291e41f4b71Sopenharmony_ci      'title': 'titleOnCreate',
292e41f4b71Sopenharmony_ci      'detail': 'detailOnCreate'
293e41f4b71Sopenharmony_ci    };
294e41f4b71Sopenharmony_ci    let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
295e41f4b71Sopenharmony_ci    return formData;
296e41f4b71Sopenharmony_ci  }
297e41f4b71Sopenharmony_ci}
298e41f4b71Sopenharmony_ci```
299e41f4b71Sopenharmony_ci
300e41f4b71Sopenharmony_ciYou should override **onRemoveForm** to implement widget data deletion.
301e41f4b71Sopenharmony_ci
302e41f4b71Sopenharmony_ci
303e41f4b71Sopenharmony_ci```ts
304e41f4b71Sopenharmony_ciimport { common } from '@kit.AbilityKit';
305e41f4b71Sopenharmony_ciimport { hilog } from '@kit.PerformanceAnalysisKit';
306e41f4b71Sopenharmony_ciimport { FormExtensionAbility } from '@kit.FormKit';
307e41f4b71Sopenharmony_ciimport { BusinessError } from '@kit.BasicServicesKit';
308e41f4b71Sopenharmony_ciimport { preferences } from '@kit.ArkData';
309e41f4b71Sopenharmony_ci
310e41f4b71Sopenharmony_ciconst TAG: string = 'JsCardFormAbility';
311e41f4b71Sopenharmony_ciconst DATA_STORAGE_PATH: string = '/data/storage/el2/base/haps/form_store';
312e41f4b71Sopenharmony_ciconst DOMAIN_NUMBER: number = 0xFF00;
313e41f4b71Sopenharmony_ci
314e41f4b71Sopenharmony_cilet deleteFormInfo = async (formId: string, context: common.FormExtensionContext): Promise<void> => {
315e41f4b71Sopenharmony_ci  try {
316e41f4b71Sopenharmony_ci    const storage: preferences.Preferences = await preferences.getPreferences(context, DATA_STORAGE_PATH);
317e41f4b71Sopenharmony_ci    // Delete the widget information.
318e41f4b71Sopenharmony_ci    await storage.delete(formId);
319e41f4b71Sopenharmony_ci    hilog.info(DOMAIN_NUMBER, TAG, `[EntryFormAbility] deleteFormInfo, del form info successfully, formId: ${formId}`);
320e41f4b71Sopenharmony_ci    await storage.flush();
321e41f4b71Sopenharmony_ci  } catch (err) {
322e41f4b71Sopenharmony_ci    hilog.error(DOMAIN_NUMBER, TAG, `[EntryFormAbility] failed to deleteFormInfo, err: ${JSON.stringify(err as BusinessError)}`);
323e41f4b71Sopenharmony_ci  };
324e41f4b71Sopenharmony_ci};
325e41f4b71Sopenharmony_ci
326e41f4b71Sopenharmony_ciexport default class JsCardFormAbility extends FormExtensionAbility {
327e41f4b71Sopenharmony_ci  onRemoveForm(formId: string): void {
328e41f4b71Sopenharmony_ci    // Delete widget data.
329e41f4b71Sopenharmony_ci    hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onRemoveForm');
330e41f4b71Sopenharmony_ci    // Delete the persistent widget instance data.
331e41f4b71Sopenharmony_ci    // Implement this API based on project requirements.
332e41f4b71Sopenharmony_ci    deleteFormInfo(formId, this.context);
333e41f4b71Sopenharmony_ci  }
334e41f4b71Sopenharmony_ci}
335e41f4b71Sopenharmony_ci```
336e41f4b71Sopenharmony_ci
337e41f4b71Sopenharmony_ciFor details about how to implement persistent data storage, see [Application Data Persistence](../database/app-data-persistence-overview.md).
338e41f4b71Sopenharmony_ci
339e41f4b71Sopenharmony_ciThe **Want** object passed in by the widget host to the widget provider contains a flag that specifies whether the requested widget is normal or temporary.
340e41f4b71Sopenharmony_ci
341e41f4b71Sopenharmony_ci- Normal widget: a widget persistently used by the widget host
342e41f4b71Sopenharmony_ci
343e41f4b71Sopenharmony_ci- Temporary widget: a widget temporarily used by the widget host
344e41f4b71Sopenharmony_ci
345e41f4b71Sopenharmony_ciData of a temporary widget will be deleted on the Widget Manager if the widget framework is killed and restarted. The widget provider, however, is not notified of the deletion and still keeps the data. Therefore, the widget provider needs to clear the data of temporary widgets proactively if the data has been kept for a long period of time. If the widget host has converted a temporary widget into a normal one, the widget provider should change the widget data from temporary storage to persistent storage. Otherwise, the widget data may be deleted by mistake.
346e41f4b71Sopenharmony_ci
347e41f4b71Sopenharmony_ci
348e41f4b71Sopenharmony_ci### Updating Widget Data
349e41f4b71Sopenharmony_ci
350e41f4b71Sopenharmony_ciWhen an application initiates a scheduled or periodic update, the application obtains the latest data and calls **updateForm()** to update the widget.
351e41f4b71Sopenharmony_ci
352e41f4b71Sopenharmony_ci
353e41f4b71Sopenharmony_ci```ts
354e41f4b71Sopenharmony_ciimport { hilog } from '@kit.PerformanceAnalysisKit';
355e41f4b71Sopenharmony_ciimport { formBindingData, FormExtensionAbility, formProvider } from '@kit.FormKit';
356e41f4b71Sopenharmony_ciimport { BusinessError } from '@kit.BasicServicesKit';
357e41f4b71Sopenharmony_ci
358e41f4b71Sopenharmony_ciconst TAG: string = 'JsCardFormAbility';
359e41f4b71Sopenharmony_ciconst DOMAIN_NUMBER: number = 0xFF00;
360e41f4b71Sopenharmony_ci
361e41f4b71Sopenharmony_ciexport default class EntryFormAbility extends FormExtensionAbility {
362e41f4b71Sopenharmony_ci  onUpdateForm(formId: string): void {
363e41f4b71Sopenharmony_ci    // Override this method to support scheduled updates, periodic updates, or updates requested by the widget host.
364e41f4b71Sopenharmony_ci    hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onUpdateForm');
365e41f4b71Sopenharmony_ci    let obj: Record<string, string> = {
366e41f4b71Sopenharmony_ci      'title': 'titleOnUpdate',
367e41f4b71Sopenharmony_ci      'detail': 'detailOnUpdate'
368e41f4b71Sopenharmony_ci    };
369e41f4b71Sopenharmony_ci    let formData: formBindingData.FormBindingData = formBindingData.createFormBindingData(obj);
370e41f4b71Sopenharmony_ci    formProvider.updateForm(formId, formData).catch((error: BusinessError) => {
371e41f4b71Sopenharmony_ci      hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] updateForm, error:' + JSON.stringify(error));
372e41f4b71Sopenharmony_ci    });
373e41f4b71Sopenharmony_ci  }
374e41f4b71Sopenharmony_ci}
375e41f4b71Sopenharmony_ci```
376e41f4b71Sopenharmony_ci
377e41f4b71Sopenharmony_ci
378e41f4b71Sopenharmony_ci### Developing the Widget UI Page
379e41f4b71Sopenharmony_ci
380e41f4b71Sopenharmony_ciYou can use the web-like paradigm (HML+CSS+JSON) to develop JS widget pages. This section describes how to develop a page shown below.
381e41f4b71Sopenharmony_ci
382e41f4b71Sopenharmony_ci![WidgetCardPage](figures/WidgetCardPage.png)
383e41f4b71Sopenharmony_ci
384e41f4b71Sopenharmony_ci- HML: uses web-like paradigm components to describe the widget page information.
385e41f4b71Sopenharmony_ci
386e41f4b71Sopenharmony_ci  
387e41f4b71Sopenharmony_ci  ```html
388e41f4b71Sopenharmony_ci  <div class="container">
389e41f4b71Sopenharmony_ci    <stack>
390e41f4b71Sopenharmony_ci      <div class="container-img">
391e41f4b71Sopenharmony_ci        <image src="/common/widget.png" class="bg-img"></image>
392e41f4b71Sopenharmony_ci      </div>
393e41f4b71Sopenharmony_ci      <div class="container-inner">
394e41f4b71Sopenharmony_ci        <text class="title">{{title}}</text>
395e41f4b71Sopenharmony_ci        <text class="detail_text" onclick="routerEvent">{{detail}}</text>
396e41f4b71Sopenharmony_ci      </div>
397e41f4b71Sopenharmony_ci    </stack>
398e41f4b71Sopenharmony_ci  </div>
399e41f4b71Sopenharmony_ci  ```
400e41f4b71Sopenharmony_ci
401e41f4b71Sopenharmony_ci- CSS: defines style information about the web-like paradigm components in HML.
402e41f4b71Sopenharmony_ci
403e41f4b71Sopenharmony_ci    ```css
404e41f4b71Sopenharmony_ci    .container {
405e41f4b71Sopenharmony_ci      flex-direction: column;
406e41f4b71Sopenharmony_ci      justify-content: center;
407e41f4b71Sopenharmony_ci      align-items: center;
408e41f4b71Sopenharmony_ci    }
409e41f4b71Sopenharmony_ci    
410e41f4b71Sopenharmony_ci    .bg-img {
411e41f4b71Sopenharmony_ci      flex-shrink: 0;
412e41f4b71Sopenharmony_ci      height: 100%;
413e41f4b71Sopenharmony_ci    }
414e41f4b71Sopenharmony_ci    
415e41f4b71Sopenharmony_ci    .container-inner {
416e41f4b71Sopenharmony_ci      flex-direction: column;
417e41f4b71Sopenharmony_ci      justify-content: flex-end;
418e41f4b71Sopenharmony_ci      align-items: flex-start;
419e41f4b71Sopenharmony_ci      height: 100%;
420e41f4b71Sopenharmony_ci      width: 100%;
421e41f4b71Sopenharmony_ci      padding: 12px;
422e41f4b71Sopenharmony_ci    }
423e41f4b71Sopenharmony_ci    
424e41f4b71Sopenharmony_ci    .title {
425e41f4b71Sopenharmony_ci      font-size: 19px;
426e41f4b71Sopenharmony_ci      font-weight: bold;
427e41f4b71Sopenharmony_ci      color: white;
428e41f4b71Sopenharmony_ci      text-overflow: ellipsis;
429e41f4b71Sopenharmony_ci      max-lines: 1;
430e41f4b71Sopenharmony_ci    }
431e41f4b71Sopenharmony_ci    
432e41f4b71Sopenharmony_ci    .detail_text {
433e41f4b71Sopenharmony_ci      font-size: 16px;
434e41f4b71Sopenharmony_ci      color: white;
435e41f4b71Sopenharmony_ci      opacity: 0.66;
436e41f4b71Sopenharmony_ci      text-overflow: ellipsis;
437e41f4b71Sopenharmony_ci      max-lines: 1;
438e41f4b71Sopenharmony_ci      margin-top: 6px;
439e41f4b71Sopenharmony_ci    }
440e41f4b71Sopenharmony_ci    ```
441e41f4b71Sopenharmony_ci
442e41f4b71Sopenharmony_ci- JSON: defines data and event interaction on the widget UI page.
443e41f4b71Sopenharmony_ci
444e41f4b71Sopenharmony_ci    ```json
445e41f4b71Sopenharmony_ci    {
446e41f4b71Sopenharmony_ci      "data": {
447e41f4b71Sopenharmony_ci        "title": "TitleDefault",
448e41f4b71Sopenharmony_ci        "detail": "TextDefault"
449e41f4b71Sopenharmony_ci      },
450e41f4b71Sopenharmony_ci      "actions": {
451e41f4b71Sopenharmony_ci        "routerEvent": {
452e41f4b71Sopenharmony_ci          "action": "router",
453e41f4b71Sopenharmony_ci          "abilityName": "EntryAbility",
454e41f4b71Sopenharmony_ci          "params": {
455e41f4b71Sopenharmony_ci            "message": "add detail"
456e41f4b71Sopenharmony_ci          }
457e41f4b71Sopenharmony_ci        }
458e41f4b71Sopenharmony_ci      }
459e41f4b71Sopenharmony_ci    }
460e41f4b71Sopenharmony_ci    ```
461e41f4b71Sopenharmony_ci
462e41f4b71Sopenharmony_ci
463e41f4b71Sopenharmony_ci### Developing Widget Events
464e41f4b71Sopenharmony_ci
465e41f4b71Sopenharmony_ciYou can set router and message events for components on a widget. The router event applies to UIAbility redirection, and the message event applies to custom click events.
466e41f4b71Sopenharmony_ci
467e41f4b71Sopenharmony_ciThe key steps are as follows:
468e41f4b71Sopenharmony_ci
469e41f4b71Sopenharmony_ci1. Set the **onclick** field in the HML file to **routerEvent** or **messageEvent**, depending on the **actions** settings in the JSON file.
470e41f4b71Sopenharmony_ci
471e41f4b71Sopenharmony_ci2. Set the router event.
472e41f4b71Sopenharmony_ci
473e41f4b71Sopenharmony_ci   - **action**: **"router"**, which indicates a router event.
474e41f4b71Sopenharmony_ci   - **abilityName**: name of the UIAbility to redirect to (PageAbility component in the FA model and UIAbility component in the stage model). For example, the default UIAbility name of the stage model created by DevEco Studio is EntryAbility.
475e41f4b71Sopenharmony_ci   - **params**: custom parameters passed to the target UIAbility. Set them as required. The value can be obtained from **parameters** in **want** used for starting the target UIAbility. For example, in the lifecycle function **onCreate** of the MainAbility in the stage model, you can obtain **want** and its **parameters** field.
476e41f4b71Sopenharmony_ci
477e41f4b71Sopenharmony_ci3. Set the message event.
478e41f4b71Sopenharmony_ci
479e41f4b71Sopenharmony_ci   - **action**: **"message"**, which indicates a message event.
480e41f4b71Sopenharmony_ci   - **params**: custom parameters of the message event. Set them as required. The value can be obtained from **message** in the widget lifecycle function **onFormEvent()**.
481e41f4b71Sopenharmony_ci
482e41f4b71Sopenharmony_ciThe following are examples:
483e41f4b71Sopenharmony_ci
484e41f4b71Sopenharmony_ci- HML file:
485e41f4b71Sopenharmony_ci
486e41f4b71Sopenharmony_ci  ```html
487e41f4b71Sopenharmony_ci  <div class="container">
488e41f4b71Sopenharmony_ci      <stack>
489e41f4b71Sopenharmony_ci          <div class="container-img">
490e41f4b71Sopenharmony_ci              <image src="/common/CardWebImg.png" class="bg-img"></image>
491e41f4b71Sopenharmony_ci              <image src="/common/CardWebImgMatrix.png"
492e41f4b71Sopenharmony_ci                     class="bottom-img"/>
493e41f4b71Sopenharmony_ci          </div>
494e41f4b71Sopenharmony_ci          <div class="container-inner">
495e41f4b71Sopenharmony_ci              <text class="title" onclick="routerEvent">{{ title }}</text>
496e41f4b71Sopenharmony_ci              <text class="detail_text" onclick="messageEvent">{{ detail }}</text>
497e41f4b71Sopenharmony_ci          </div>
498e41f4b71Sopenharmony_ci      </stack>
499e41f4b71Sopenharmony_ci  </div>
500e41f4b71Sopenharmony_ci  ```
501e41f4b71Sopenharmony_ci
502e41f4b71Sopenharmony_ci- CSS file:
503e41f4b71Sopenharmony_ci
504e41f4b71Sopenharmony_ci    ```css
505e41f4b71Sopenharmony_ci    .container {
506e41f4b71Sopenharmony_ci        flex-direction: column;
507e41f4b71Sopenharmony_ci        justify-content: center;
508e41f4b71Sopenharmony_ci        align-items: center;
509e41f4b71Sopenharmony_ci    }
510e41f4b71Sopenharmony_ci      
511e41f4b71Sopenharmony_ci    .bg-img {
512e41f4b71Sopenharmony_ci        flex-shrink: 0;
513e41f4b71Sopenharmony_ci        height: 100%;
514e41f4b71Sopenharmony_ci        z-index: 1;
515e41f4b71Sopenharmony_ci    }
516e41f4b71Sopenharmony_ci      
517e41f4b71Sopenharmony_ci    .bottom-img {
518e41f4b71Sopenharmony_ci        position: absolute;
519e41f4b71Sopenharmony_ci        width: 150px;
520e41f4b71Sopenharmony_ci        height: 56px;
521e41f4b71Sopenharmony_ci        top: 63%;
522e41f4b71Sopenharmony_ci        background-color: rgba(216, 216, 216, 0.15);
523e41f4b71Sopenharmony_ci        filter: blur(20px);
524e41f4b71Sopenharmony_ci        z-index: 2;
525e41f4b71Sopenharmony_ci    }
526e41f4b71Sopenharmony_ci      
527e41f4b71Sopenharmony_ci    .container-inner {
528e41f4b71Sopenharmony_ci        flex-direction: column;
529e41f4b71Sopenharmony_ci        justify-content: flex-end;
530e41f4b71Sopenharmony_ci        align-items: flex-start;
531e41f4b71Sopenharmony_ci        height: 100%;
532e41f4b71Sopenharmony_ci        width: 100%;
533e41f4b71Sopenharmony_ci        padding: 12px;
534e41f4b71Sopenharmony_ci    }
535e41f4b71Sopenharmony_ci      
536e41f4b71Sopenharmony_ci    .title {
537e41f4b71Sopenharmony_ci        font-family: HarmonyHeiTi-Medium;
538e41f4b71Sopenharmony_ci        font-size: 14px;
539e41f4b71Sopenharmony_ci        color: rgba(255, 255, 255, 0.90);
540e41f4b71Sopenharmony_ci        letter-spacing: 0.6px;
541e41f4b71Sopenharmony_ci        font-weight: 500;
542e41f4b71Sopenharmony_ci        width: 100%;
543e41f4b71Sopenharmony_ci        text-overflow: ellipsis;
544e41f4b71Sopenharmony_ci        max-lines: 1;
545e41f4b71Sopenharmony_ci    }
546e41f4b71Sopenharmony_ci      
547e41f4b71Sopenharmony_ci    .detail_text {
548e41f4b71Sopenharmony_ci        ffont-family: HarmonyHeiTi;
549e41f4b71Sopenharmony_ci        font-size: 12px;
550e41f4b71Sopenharmony_ci        color: rgba(255, 255, 255, 0.60);
551e41f4b71Sopenharmony_ci        letter-spacing: 0.51px;
552e41f4b71Sopenharmony_ci        font-weight: 400;
553e41f4b71Sopenharmony_ci        text-overflow: ellipsis;
554e41f4b71Sopenharmony_ci        max-lines: 1;
555e41f4b71Sopenharmony_ci        margin-top: 6px;
556e41f4b71Sopenharmony_ci        width: 100%;
557e41f4b71Sopenharmony_ci    }
558e41f4b71Sopenharmony_ci    ```
559e41f4b71Sopenharmony_ci
560e41f4b71Sopenharmony_ci- JSON file:
561e41f4b71Sopenharmony_ci
562e41f4b71Sopenharmony_ci    ```json
563e41f4b71Sopenharmony_ci    {
564e41f4b71Sopenharmony_ci      "data": {
565e41f4b71Sopenharmony_ci        "title": "TitleDefault",
566e41f4b71Sopenharmony_ci        "detail": "TextDefault"
567e41f4b71Sopenharmony_ci      },
568e41f4b71Sopenharmony_ci      "actions": {
569e41f4b71Sopenharmony_ci        "routerEvent": {
570e41f4b71Sopenharmony_ci          "action": "router",
571e41f4b71Sopenharmony_ci          "abilityName": "JSCardEntryAbility",
572e41f4b71Sopenharmony_ci          "params": {
573e41f4b71Sopenharmony_ci            "info": "router info",
574e41f4b71Sopenharmony_ci            "message": "router message"
575e41f4b71Sopenharmony_ci          }
576e41f4b71Sopenharmony_ci        },
577e41f4b71Sopenharmony_ci        "messageEvent": {
578e41f4b71Sopenharmony_ci          "action": "message",
579e41f4b71Sopenharmony_ci          "params": {
580e41f4b71Sopenharmony_ci            "detail": "message detail"
581e41f4b71Sopenharmony_ci          }
582e41f4b71Sopenharmony_ci        }
583e41f4b71Sopenharmony_ci      }
584e41f4b71Sopenharmony_ci    }
585e41f4b71Sopenharmony_ci    ```
586e41f4b71Sopenharmony_ci
587e41f4b71Sopenharmony_ci  > **NOTE**
588e41f4b71Sopenharmony_ci  >
589e41f4b71Sopenharmony_ci  > **JSON Value** in **data** supports multi-level nested data. When updating data, ensure that complete data is carried.
590e41f4b71Sopenharmony_ci
591e41f4b71Sopenharmony_ciAssume that a widget is displaying the course information of Mr. Zhang on July 18, as shown in the following code snippet.
592e41f4b71Sopenharmony_ci  ```ts
593e41f4b71Sopenharmony_ci  "data": {
594e41f4b71Sopenharmony_ci      "Day": "07.18",
595e41f4b71Sopenharmony_ci      "teacher": {
596e41f4b71Sopenharmony_ci          "name": "Mr.Zhang",
597e41f4b71Sopenharmony_ci          "course": "Math"
598e41f4b71Sopenharmony_ci      }
599e41f4b71Sopenharmony_ci  }
600e41f4b71Sopenharmony_ci  ```
601e41f4b71Sopenharmony_ciTo update the widget content to the course information of Mr. Li on July 18, you must pass the complete data as follows, instead of only a single date item such as **name** or **course**:
602e41f4b71Sopenharmony_ci  ```ts
603e41f4b71Sopenharmony_ci  "teacher": {
604e41f4b71Sopenharmony_ci      "name": "Mr.Li",
605e41f4b71Sopenharmony_ci      "course": "English"
606e41f4b71Sopenharmony_ci  }
607e41f4b71Sopenharmony_ci  ```
608e41f4b71Sopenharmony_ci
609e41f4b71Sopenharmony_ci
610e41f4b71Sopenharmony_ci- Receive the router event in UIAbility and obtain parameters.
611e41f4b71Sopenharmony_ci
612e41f4b71Sopenharmony_ci    ```ts
613e41f4b71Sopenharmony_ci    import UIAbility from '@ohos.app.ability.UIAbility';
614e41f4b71Sopenharmony_ci    import AbilityConstant from '@ohos.app.ability.AbilityConstant';
615e41f4b71Sopenharmony_ci    import Want from '@ohos.app.ability.Want';
616e41f4b71Sopenharmony_ci    import hilog from '@ohos.hilog';
617e41f4b71Sopenharmony_ci      
618e41f4b71Sopenharmony_ci    const TAG: string = 'EtsCardEntryAbility';
619e41f4b71Sopenharmony_ci    const DOMAIN_NUMBER: number = 0xFF00;
620e41f4b71Sopenharmony_ci      
621e41f4b71Sopenharmony_ci    export default class EtsCardEntryAbility extends UIAbility {
622e41f4b71Sopenharmony_ci      onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
623e41f4b71Sopenharmony_ci        if (want.parameters) {
624e41f4b71Sopenharmony_ci          let params: Record<string, Object> = JSON.parse(JSON.stringify(want.parameters.params));
625e41f4b71Sopenharmony_ci          // Obtain the info parameter passed in the router event.
626e41f4b71Sopenharmony_ci          if (params.info === 'router info') {
627e41f4b71Sopenharmony_ci            // Execute the service logic.
628e41f4b71Sopenharmony_ci            hilog.info(DOMAIN_NUMBER, TAG, `router info: ${params.info}`);
629e41f4b71Sopenharmony_ci          }
630e41f4b71Sopenharmony_ci          // Obtain the message parameter passed in the router event.
631e41f4b71Sopenharmony_ci          if (params.message === 'router message') {
632e41f4b71Sopenharmony_ci            // Execute the service logic.
633e41f4b71Sopenharmony_ci            hilog.info(DOMAIN_NUMBER, TAG, `router message: ${params.message}`);
634e41f4b71Sopenharmony_ci          }
635e41f4b71Sopenharmony_ci        }
636e41f4b71Sopenharmony_ci      }
637e41f4b71Sopenharmony_ci    };
638e41f4b71Sopenharmony_ci    ```
639e41f4b71Sopenharmony_ci
640e41f4b71Sopenharmony_ci- Receive the message event in FormExtensionAbility and obtain parameters.
641e41f4b71Sopenharmony_ci
642e41f4b71Sopenharmony_ci    ```ts
643e41f4b71Sopenharmony_ci    import FormExtension from '@ohos.app.form.FormExtensionAbility';
644e41f4b71Sopenharmony_ci    import hilog from '@ohos.hilog';
645e41f4b71Sopenharmony_ci    
646e41f4b71Sopenharmony_ci    const TAG: string = 'FormAbility';
647e41f4b71Sopenharmony_ci    const DOMAIN_NUMBER: number = 0xFF00;
648e41f4b71Sopenharmony_ci    
649e41f4b71Sopenharmony_ci    export default class FormAbility extends FormExtension {
650e41f4b71Sopenharmony_ci      onFormEvent(formId: string, message: string): void {
651e41f4b71Sopenharmony_ci        // If the widget supports event triggering, override this method and implement the trigger.
652e41f4b71Sopenharmony_ci        hilog.info(DOMAIN_NUMBER, TAG, '[EntryFormAbility] onFormEvent');
653e41f4b71Sopenharmony_ci        // Obtain the detail parameter passed in the message event.
654e41f4b71Sopenharmony_ci        let msg: Record<string, string> = JSON.parse(message);
655e41f4b71Sopenharmony_ci        if (msg.detail === 'message detail') {
656e41f4b71Sopenharmony_ci          // Execute the service logic.
657e41f4b71Sopenharmony_ci          hilog.info(DOMAIN_NUMBER, TAG, 'message info:' + msg.detail);
658e41f4b71Sopenharmony_ci        }
659e41f4b71Sopenharmony_ci      }
660e41f4b71Sopenharmony_ci    };
661e41f4b71Sopenharmony_ci    ```