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