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 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 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 ```