1# Updating Widget Content by State
2
3There are cases where multiple copies of the same widget are added to the home screen to accommodate different needs. In these cases, the widget content needs to be dynamically updated based on the state. This topic exemplifies how this is implemented.
4
5In the following example, two copies of the weather widget are added to the home screen: one for displaying the weather of London, and the other Beijing, both configured to be updated at 07:00 every morning. The widget provider detects the target city, and then displays the city-specific weather information on the widgets.
6
7
8- Widget configuration file: Configure the widget to be automatically updated every 30 minutes.
9
10  ```json
11  {
12    "forms": [
13      {
14        "name": "WidgetUpdateByStatus",
15        "description": "$string:UpdateByStatusFormAbility_desc",
16        "src": "./ets/widgetupdatebystatus/pages/WidgetUpdateByStatusCard.ets",
17        "uiSyntax": "arkts",
18        "window": {
19          "designWidth": 720,
20          "autoDesignWidth": true
21        },
22        "colorMode": "auto",
23        "isDefault": true,
24        "updateEnabled": true,
25        "scheduledUpdateTime": "10:30",
26        "updateDuration": 1,
27        "defaultDimension": "2*2",
28        "supportDimensions": [
29          "2*2"
30        ]
31      }
32    ]
33  }
34  ```
35
36- Widget page: A widget has different states and needs to be updated by state. When the state changes, **postCardAction** is called to notify the EntryFormAbility.
37
38  ```ts
39  let storageUpdateByStatus = new LocalStorage();
40
41  @Entry(storageUpdateByStatus)
42  @Component
43  struct WidgetUpdateByStatusCard {
44    @LocalStorageProp('textA') textA: Resource = $r('app.string.to_be_refreshed');
45    @LocalStorageProp('textB') textB: Resource = $r('app.string.to_be_refreshed');
46    @State selectA: boolean = false;
47    @State selectB: boolean = false;
48
49    build() {
50      Column() {
51        Column() {
52          Row() {
53            Checkbox({ name: 'checkbox1', group: 'checkboxGroup' })
54              .padding(0)
55              .select(false)
56              .margin({ left: 26 })
57              .onChange((value: boolean) => {
58                this.selectA = value;
59                postCardAction(this, {
60                  action: 'message',
61                  params: {
62                    selectA: JSON.stringify(value)
63                  }
64                });
65              })
66            Text($r('app.string.status_a'))
67              .fontColor('#000000')
68              .opacity(0.9)
69              .fontSize(14)
70              .margin({ left: 8 })
71          }
72          .width('100%')
73          .padding(0)
74          .justifyContent(FlexAlign.Start)
75
76          Row() {
77            Checkbox({ name: 'checkbox2', group: 'checkboxGroup' })
78              .padding(0)
79              .select(false)
80              .margin({ left: 26 })
81              .onChange((value: boolean) => {
82                this.selectB = value;
83                postCardAction(this, {
84                  action: 'message',
85                  params: {
86                    selectB: JSON.stringify(value)
87                  }
88                });
89              })
90            Text($r('app.string.status_b'))
91              .fontColor('#000000')
92              .opacity(0.9)
93              .fontSize(14)
94              .margin({ left: 8 })
95          }
96          .width('100%')
97          .position({ y: 32 })
98          .padding(0)
99          .justifyContent(FlexAlign.Start)
100        }
101        .position({ y: 12 })
102
103        Column() {
104          Row() { // Content that is updated only in state A
105            Text($r('app.string.status_a'))
106              .fontColor('#000000')
107              .opacity(0.4)
108              .fontSize(12)
109
110            Text(this.textA)
111              .fontColor('#000000')
112              .opacity(0.4)
113              .fontSize(12)
114          }
115          .margin({ top: '12px', left: 26, right: '26px' })
116
117          Row() { // Content that is updated only in state B
118            Text($r('app.string.status_b'))
119              .fontColor('#000000')
120              .opacity(0.4)
121              .fontSize(12)
122            Text(this.textB)
123              .fontColor('#000000')
124              .opacity(0.4)
125              .fontSize(12)
126          }.margin({ top: '12px', bottom: '21px', left: 26, right: '26px' })
127        }
128        .margin({ top: 80 })
129        .width('100%')
130        .alignItems(HorizontalAlign.Start)
131      }.width('100%').height('100%')
132      .backgroundImage($r('app.media.CardUpdateByStatus'))
133      .backgroundImageSize(ImageSize.Cover)
134    }
135  }
136  ```
137
138- EntryFormAbility: The widget state data is stored in the local database. When the update event callback is triggered, the current widget state is obtained through **formId**, and then content is updated based on the state obtained.
139
140  ```ts
141  import { Want } from '@kit.AbilityKit';
142  import { preferences } from '@kit.ArkData';
143  import { BusinessError } from '@kit.BasicServicesKit';
144  import { formBindingData, FormExtensionAbility, formInfo, formProvider } from '@kit.FormKit';
145  import { hilog } from '@kit.PerformanceAnalysisKit';
146
147  const TAG: string = 'UpdateByStatusFormAbility';
148  const DOMAIN_NUMBER: number = 0xFF00;
149
150  export default class UpdateByStatusFormAbility extends FormExtensionAbility {
151    onAddForm(want: Want): formBindingData.FormBindingData {
152      let formId: string = '';
153      let isTempCard: boolean;
154      if (want.parameters) {
155        formId = want.parameters[formInfo.FormParam.IDENTITY_KEY].toString();
156        isTempCard = want.parameters[formInfo.FormParam.TEMPORARY_KEY] as boolean;
157        if (isTempCard === false) { // If the widget is a normal one, the widget information is persisted.
158          hilog.info(DOMAIN_NUMBER, TAG, 'Not temp card, init db for:' + formId);
159          let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
160          promise.then(async (storeDB: preferences.Preferences) => {
161            hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
162            await storeDB.put('A' + formId, 'false');
163            await storeDB.put('B' + formId, 'false');
164            await storeDB.flush();
165          }).catch((err: BusinessError) => {
166            hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
167          });
168        }
169    }
170      let formData: Record<string, Object | string> = {};
171      return formBindingData.createFormBindingData(formData);
172    }
173
174    onRemoveForm(formId: string): void {
175      hilog.info(DOMAIN_NUMBER, TAG, 'onRemoveForm, formId:' + formId);
176      let promise = preferences.getPreferences(this.context, 'myStore');
177      promise.then(async (storeDB) => {
178        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
179        await storeDB.delete('A' + formId);
180        await storeDB.delete('B' + formId);
181      }).catch((err: BusinessError) => {
182      hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
183      });
184    }
185
186    // If the widget is a temporary one, it is recommended that the widget information be persisted when the widget is converted to a normal one.
187    onCastToNormalForm(formId: string): void {
188      hilog.info(DOMAIN_NUMBER, TAG, 'onCastToNormalForm, formId:' + formId);
189      let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
190      promise.then(async (storeDB: preferences.Preferences) => {
191        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
192        await storeDB.put('A' + formId, 'false');
193        await storeDB.put('B' + formId, 'false');
194        await storeDB.flush();
195      }).catch((err: BusinessError) => {
196      hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
197      });
198    }
199
200    onUpdateForm(formId: string): void {
201      let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
202      promise.then(async (storeDB: preferences.Preferences) => {
203        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences from onUpdateForm.');
204        let stateA = await storeDB.get('A' + formId, 'false');
205        let stateB = await storeDB.get('B' + formId, 'false');
206        // Update textA in state A.
207        if (stateA === 'true') {
208          let param: Record<string, string> = {
209            'textA': 'AAA'
210          };
211          let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
212          await formProvider.updateForm(formId, formInfo);
213        }
214        // Update textB in state B.
215        if (stateB === 'true') {
216          let param: Record<string, string> = {
217            'textB': 'BBB'
218          };
219          let formInfo: formBindingData.FormBindingData = formBindingData.createFormBindingData(param);
220        await formProvider.updateForm(formId, formInfo);
221        }
222        hilog.info(DOMAIN_NUMBER, TAG, `Update form success stateA:${stateA} stateB:${stateB}.`);
223      }).catch((err: BusinessError) => {
224        hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
225      });
226    }
227
228    onFormEvent(formId: string, message: string): void {
229      // Store the widget state.
230      hilog.info(DOMAIN_NUMBER, TAG, 'onFormEvent formId:' + formId + 'msg:' + message);
231      let promise: Promise<preferences.Preferences> = preferences.getPreferences(this.context, 'myStore');
232      promise.then(async (storeDB: preferences.Preferences) => {
233        hilog.info(DOMAIN_NUMBER, TAG, 'Succeeded to get preferences.');
234        let msg: Record<string, string> = JSON.parse(message);
235        if (msg.selectA !== undefined) {
236          hilog.info(DOMAIN_NUMBER, TAG, 'onFormEvent selectA info:' + msg.selectA);
237          await storeDB.put('A' + formId, msg.selectA);
238        }
239        if (msg.selectB !== undefined) {
240          hilog.info(DOMAIN_NUMBER, TAG, 'onFormEvent selectB info:' + msg.selectB);
241          await storeDB.put('B' + formId, msg.selectB);
242        }
243        await storeDB.flush();
244      }).catch((err: BusinessError) => {
245        hilog.info(DOMAIN_NUMBER, TAG, `Failed to get preferences. ${JSON.stringify(err)}`);
246      });
247    }
248  }
249  ```
250
251
252> **NOTE**
253>
254> When the local database is used for widget information persistence, it is recommended that [TEMPORARY_KEY](../reference/apis-form-kit/js-apis-app-form-formInfo.md#formparam) be used in the [onAddForm](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md#onaddform) lifecycle callback to determine whether the currently added widget is a normal one. If the widget is a normal one, the widget information is directly persisted. If the widget is a temporary one, the widget information is persisted when the widget is converted to a normal one ([onCastToNormalForm](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md#oncasttonormalform)). In addition, the persistent widget information needs to be deleted when the widget is destroyed ([onRemoveForm](../reference/apis-form-kit/js-apis-app-form-formExtensionAbility.md#onremoveform)), preventing the database size from continuously increasing due to repeated widget addition and deletion.
255