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