1# Updating Widget Content Through the router or call Event
2
3
4On the widget page, the [postCardAction](../reference/apis-arkui/js-apis-postCardAction.md#postcardaction) API can be used to trigger a router or call event to start a UIAbility, which then updates the widget content. The following is an example of this widget update mode.
5
6> **NOTE**
7>
8> This topic describes development for dynamic widgets. For static widgets, see [FormLink](../reference/apis-arkui/arkui-ts/ts-container-formlink.md).
9
10## Updating Widget Content Through the router Event
11
12- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the router event to start the UIAbility.
13
14  ```ts
15  let storageUpdateRouter = new LocalStorage();
16
17  @Entry(storageUpdateRouter)
18  @Component
19  struct WidgetUpdateRouterCard {
20    @LocalStorageProp('routerDetail') routerDetail: ResourceStr = $r('app.string.init');
21
22    build() {
23      Column() {
24        Column() {
25          Text(this.routerDetail)
26            .fontColor('#FFFFFF')
27            .opacity(0.9)
28            .fontSize(14)
29            .margin({ top: '8%', left: '10%', right: '10%' })
30            .textOverflow({ overflow: TextOverflow.Ellipsis })
31            .maxLines(2)
32        }.width('100%').height('50%')
33        .alignItems(HorizontalAlign.Start)
34
35        Row() {
36          Button() {
37            Text($r('app.string.JumpLabel'))
38              .fontColor('#45A6F4')
39              .fontSize(12)
40          }
41          .width(120)
42          .height(32)
43          .margin({ top: '30%', bottom: '10%' })
44          .backgroundColor('#FFFFFF')
45          .borderRadius(16)
46          .onClick(() => {
47            postCardAction(this, {
48              action: 'router',
49              abilityName: 'WidgetEventRouterEntryAbility', // Only the UIAbility of the current application is allowed.
50              params: {
51                routerDetail: 'RouterFromCard',
52              }
53            });
54          })
55        }.width('100%').height('40%')
56        .justifyContent(FlexAlign.Center)
57      }
58      .width('100%')
59      .height('100%')
60      .alignItems(HorizontalAlign.Start)
61      .backgroundImage($r('app.media.CardEvent'))
62      .backgroundImageSize(ImageSize.Cover)
63    }
64  }
65  ```
66
67- In the **onCreate** or **onNewWant** lifecycle callback of the UIAbility, use the input parameter **want** to obtain the ID (**formID**) and other information of the widget, and then call the [updateForm](../reference/apis-form-kit/js-apis-app-form-formProvider.md#updateform) API to update the widget.
68
69  ```ts
70  import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
71  import { window } from '@kit.ArkUI';
72  import { BusinessError } from '@kit.BasicServicesKit';
73  import { formBindingData, formInfo, formProvider } from '@kit.FormKit';
74  import { hilog } from '@kit.PerformanceAnalysisKit';
75
76  const TAG: string = 'WidgetEventRouterEntryAbility';
77  const DOMAIN_NUMBER: number = 0xFF00;
78
79  export default class WidgetEventRouterEntryAbility extends UIAbility {
80    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
81      this.handleFormRouterEvent(want, 'onCreate');
82    }
83
84    handleFormRouterEvent(want: Want, source: string): void {
85      hilog.info(DOMAIN_NUMBER, TAG, `handleFormRouterEvent ${source}, Want: ${JSON.stringify(want)}`);
86      if (want.parameters && want.parameters[formInfo.FormParam.IDENTITY_KEY] !== undefined) {
87        let curFormId = want.parameters[formInfo.FormParam.IDENTITY_KEY].toString();
88        // want.parameters.params corresponds to params in postCardAction().
89        let message: string = (JSON.parse(want.parameters?.params as string))?.routerDetail;
90        hilog.info(DOMAIN_NUMBER, TAG, `UpdateForm formId: ${curFormId}, message: ${message}`);
91        let formData: Record<string, string> = {
92          'routerDetail': message + ' ' + source + ' UIAbility', // It matches the widget layout.
93        };
94        let formMsg = formBindingData.createFormBindingData(formData);
95        formProvider.updateForm(curFormId, formMsg).then((data) => {
96          hilog.info(DOMAIN_NUMBER, TAG, 'updateForm success.', JSON.stringify(data));
97        }).catch((error: BusinessError) => {
98          hilog.info(DOMAIN_NUMBER, TAG, 'updateForm failed.', JSON.stringify(error));
99        });
100      }
101    }
102
103    // If the UIAbility is running in the background, onNewWant is triggered after the router event is received.
104    onNewWant(want: Want, launchParam: AbilityConstant.LaunchParam): void {
105      hilog.info(DOMAIN_NUMBER, TAG, 'onNewWant Want:', JSON.stringify(want));
106      this.handleFormRouterEvent(want, 'onNewWant');
107    }
108
109    onWindowStageCreate(windowStage: window.WindowStage): void {
110      // Main window is created, set main page for this ability
111      hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');
112
113      windowStage.loadContent('pages/Index', (err, data) => {
114        if (err.code) {
115          hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
116          return;
117        }
118        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
119      });
120    }
121    // ...
122  }
123  ```
124
125
126## Updating Widget Content Through the call Event
127
128- When using the call event of the **postCardAction** API, the value of **formId** must be updated in the **onAddForm** callback of the FormExtensionAbility.
129
130  ```ts
131  import { Want } from '@kit.AbilityKit';
132  import { formBindingData, FormExtensionAbility } from '@kit.FormKit';
133
134  export default class WidgetCalleeFormAbility extends FormExtensionAbility {
135    onAddForm(want: Want): formBindingData.FormBindingData {
136      class DataObj1 {
137        formId: string = '';
138      }
139
140      let dataObj1 = new DataObj1();
141      if (want.parameters && want.parameters['ohos.extra.param.key.form_identity'] !== undefined) {
142        let formId: string = want.parameters['ohos.extra.param.key.form_identity'].toString();
143        dataObj1.formId = formId;
144      }
145      let obj1 = formBindingData.createFormBindingData(dataObj1);
146      return obj1;
147    }
148    // ...
149  }
150  ```
151
152- On the widget page, register the **onClick** event callback of the button and call the **postCardAction** API in the callback to trigger the call event to start the UIAbility.
153
154  ```ts
155  let storageUpdateCall = new LocalStorage();
156
157  @Entry(storageUpdateCall)
158  @Component
159  struct WidgetUpdateCallCard {
160    @LocalStorageProp('formId') formId: string = '12400633174999288';
161    @LocalStorageProp('calleeDetail') calleeDetail: ResourceStr = $r('app.string.init');
162
163    build() {
164      Column() {
165        Column() {
166            Text(this.calleeDetail)
167            .fontColor('#FFFFFF')
168            .opacity(0.9)
169            .fontSize(14)
170            .margin({ top: '8%', left: '10%' })
171        }.width('100%').height('50%')
172        .alignItems(HorizontalAlign.Start)
173
174        Row() {
175          Button() {
176            Text($r('app.string.CalleeJumpLabel'))
177              .fontColor('#45A6F4')
178              .fontSize(12)
179          }
180          .width(120)
181          .height(32)
182          .margin({ top: '30%', bottom: '10%' })
183          .backgroundColor('#FFFFFF')
184          .borderRadius(16)
185          .onClick(() => {
186            postCardAction(this, {
187              action: 'call',
188              abilityName: 'WidgetCalleeEntryAbility', // Only the UIAbility of the current application is allowed.
189              params: {
190                method: 'funA',
191                formId: this.formId,
192                calleeDetail: 'CallFrom'
193              }
194            });
195          })
196        }.width('100%').height('40%')
197        .justifyContent(FlexAlign.Center)
198      }
199      .width('100%')
200      .height('100%')
201      .alignItems(HorizontalAlign.Start)
202      .backgroundImage($r('app.media.CardEvent'))
203      .backgroundImageSize(ImageSize.Cover)
204    }
205  }
206  ```
207
208- Listen for the method required by the call event in the **onCreate** callback of the UIAbility, and then call the [updateForm](../reference/apis-form-kit/js-apis-app-form-formProvider.md#updateform) API in the corresponding method to update the widget.
209
210  ```ts
211  import { AbilityConstant, UIAbility, Want } from '@kit.AbilityKit';
212  import { window } from '@kit.ArkUI';
213  import { BusinessError } from '@kit.BasicServicesKit';
214  import { formBindingData, formProvider } from '@kit.FormKit';
215  import { rpc } from '@kit.IPCKit';
216  import { hilog } from '@kit.PerformanceAnalysisKit';
217
218  const TAG: string = 'WidgetCalleeEntryAbility';
219  const DOMAIN_NUMBER: number = 0xFF00;
220  const MSG_SEND_METHOD: string = 'funA';
221  const CONST_NUMBER_1: number = 1;
222
223  class MyParcelable implements rpc.Parcelable {
224    num: number;
225    str: string;
226
227    constructor(num: number, str: string) {
228      this.num = num;
229      this.str = str;
230    };
231
232    marshalling(messageSequence: rpc.MessageSequence): boolean {
233      messageSequence.writeInt(this.num);
234      messageSequence.writeString(this.str);
235      return true;
236    };
237
238    unmarshalling(messageSequence: rpc.MessageSequence): boolean {
239      this.num = messageSequence.readInt();
240      this.str = messageSequence.readString();
241      return true;
242    };
243  }
244
245  // After the call event is received, the method listened for by the callee is triggered.
246  let funACall = (data: rpc.MessageSequence): MyParcelable => {
247    // Obtain all parameters transferred in the call event.
248    let params: Record<string, string> = JSON.parse(data.readString());
249    if (params.formId !== undefined) {
250      let curFormId: string = params.formId;
251      let message: string = params.calleeDetail;
252      hilog.info(DOMAIN_NUMBER, TAG, `UpdateForm formId: ${curFormId}, message: ${message}`);
253      let formData: Record<string, string> = {
254        'calleeDetail': message
255      };
256      let formMsg: formBindingData.FormBindingData = formBindingData.createFormBindingData(formData);
257      formProvider.updateForm(curFormId, formMsg).then((data) => {
258        hilog.info(DOMAIN_NUMBER, TAG, `updateForm success. ${JSON.stringify(data)}`);
259      }).catch((error: BusinessError) => {
260        hilog.error(DOMAIN_NUMBER, TAG, `updateForm failed: ${JSON.stringify(error)}`);
261      });
262    }
263    return new MyParcelable(CONST_NUMBER_1, 'aaa');
264  };
265
266  export default class WidgetCalleeEntryAbility extends UIAbility {
267    onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void {
268      try {
269        // Listen for the method required by the call event.
270        this.callee.on(MSG_SEND_METHOD, funACall);
271      } catch (error) {
272        hilog.error(DOMAIN_NUMBER, TAG, `${MSG_SEND_METHOD} register failed with error ${JSON.stringify(error)}`);
273      }
274    }
275
276    onWindowStageCreate(windowStage: window.WindowStage): void {
277      // Main window is created, set main page for this ability
278      hilog.info(DOMAIN_NUMBER, TAG, '%{public}s', 'Ability onWindowStageCreate');
279
280      windowStage.loadContent('pages/Index', (err, data) => {
281        if (err.code) {
282          hilog.error(DOMAIN_NUMBER, TAG, 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');
283          return;
284        }
285        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded in loading the content. Data: %{public}s', JSON.stringify(data) ?? '');
286      });
287    }
288  }
289  ```
290