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