1# \@Provider and \@Consumer Decorators: Synchronizing Across Component Levels in a Two-Way Manner 2 3\@Provider and \@Consumer are used for synchronizing data across the component levels in a two-way manner, so that you are free from the constraints of the component levels. 4\@Provider and \@Consumer are decorators in state management V2, so they can be used only in \@ComponentV2. A compilation error will be reported if they are used in \@Component. 5 6>**NOTE** 7> 8>\@Provider and \@Consumer decorators are supported since API version 12. 9> 10>State management V2 is still under development, and some features may be incomplete or not always work as expected. 11 12## Overview 13 14\@Provider provides data. Its child components can use the \@Consumer to obtain the data by binding the same **key**. 15\@Consumer obtains data. It can obtain the \@Provider data of the nearest parent node by binding the same **key**. If the \@Provider data cannot be found, the local default value will be used. 16Data types decorated by \@Provider and \@Consumer must be the same. 17 18 19The following notes must be paid attention to when using \@Provider and \@Consumer: 20- \@Provider and \@Consumer both depend on the custom component levels. \@Consumer decorated component will be initialized to different values due to different parent components. 21- Using \@Provider with \@Consumer is equivalent to bonding components together. From the perspective of independent component, usage of \@Provider and \@Consumer should be lessened. 22 23 24## Capability Comparison: \@Provider and \@Consumer Vs. \@Provide and \@Consume 25In state management V1, [\@Provide and \@Consume](./arkts-provide-and-consume.md) are the decorators which provide two-way synchronization across component levels. This topic introduces \@Provider and \@Consumer decorators in state management V2. Although the names and features of the two pairs are similar, there are still some differences. 26If you are not familiar with \@Provide and \@Consume in state management V1, please skip this section. 27 28| Capability| Decorators in V2: \@Provider and \@Consumer |Decorators in V1: \@Provide and \@Consume| 29| ------------------ | ----------------------------------------------------- |----------------------------------------------------- | 30| \@Consume(r) |Local initialization is allowed. Local default value will be used when \@Provider is not found.| Local initialization is forbidden. An exception will be thrown when the \@Provide is not found.| 31| Supported type | **function** is supported.| **function** is not supported.| 32| Observation capability | Only the value change of itself can be observed. To observe the nesting scenario, use this decorator together with [\@Trace](https://gitee.com/openharmony/docs/blob/master/en/application-dev/quick-start/arkts-new-observedV2-and-trace.md).| Changes at the first layer can be observed. To observe the nesting scenario, use this decorator together with [\@Observed and \@ObjectLink](https://gitee.com/openharmony/docs/blob/master/en/application-dev/quick-start/arkts-observed-and-objectlink.md).| 33| alias and attribute name | **alias** is the unique matching **key** and the default attribute name.| If both the **alias** and attribute name are **key**, the former one is matched first. If no match is found, the attribute name can be matched.| 34| \@Provide(r) initialization from the parent component | Forbidden.| Allowed.| 35| \@Provide(r) overloading support | Enabled by default. That is, \@Provider can have duplicate names and \@Consumer can search upwards for the nearest \@Provider.| Disabled by default. That is, \@Provide with duplicate names is not allowed in the component tree. If overloading is required, set **allowOverride**.| 36 37 38## Decorator Description 39 40### Basic Rules 41\@Provider syntax: 42`@Provider(alias?: string) varName : varType = initValue` 43 44| \@Provider Property Decorator| Description | 45| ------------------ | ----------------------------------------------------- | 46| Decorator parameters | **aliasName?: string**: alias. The default value is the attribute name.| 47| Supported type | Member variables in the custom component. Property types include number, string, boolean, class, Array, Date, Map, and Set. The [arrow function](#decorating-callback-by-using-provider-and-consumer-and-facilitating-behavior-abstraction-between-components) can be decorated.| 48| Initialization from the parent component | Forbidden.| 49| Local initialization | Required.| 50| Observation capability | Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Consumer.| 51 52\@Consumer syntax: 53`@Consumer(alias?: string) varName : varType = initValue` 54 55 56| \@Consumer Property Decorator| Description | 57| --------------------- | ------------------------------------------------------------ | 58| Decorator parameters | **aliasName?: string**: alias. The default value is the attribute name. The nearest \@Provider is searched upwards. | 59| Allowed variable types | Member variables in the custom component. Property types include number, string, boolean, class, Array, Date, Map, and Set. The arrow function can be decorated.| 60| Initialization from the parent component | Forbidden.| 61| Local initialization | Required.| 62| Observation capability | Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Provider.| 63 64### aliasName and Attribute Name 65\@Provider and \@Consumer accept the optional parameter **aliasName**. If the parameter is not set, the attribute name will be used as the default value. Note: **aliasName** is the unique key for \@Provider and \@Consumer matching. 66 67The following three examples describe how \@Provider and \@Consumer use **aliasName** to search for relationships. 68```ts 69@ComponentV2 struct Parent { 70 @Provider() str: string = 'hello'; // no aliasName, use propertyName "str" as aliasName 71} 72 73@ComponentV2 struct Child { 74 @Consumer('str') str: string = 'world'; // use aliasName 'str' to find 75 // can find in Parent, use Provider value 'hello' 76} 77``` 78 79```ts 80@ComponentV2 struct Parent { 81 @Provider('alias') str: string = 'hello'; // has alias 82} 83 84@ComponentV2 struct Child { 85 @Consumer('alias') str: string = 'world'; // use aliasName 'alias' to find Provider value 'hello' 86} 87``` 88 89```ts 90@ComponentV2 struct Parent { 91 @Provider('alias') str: string = 'hello'; // has alias 92} 93 94@ComponentV2 struct Child { 95 @Consumer() str: string = 'world'; // no aliasName, use propertyName "str" as aliasName, cannot find Provider, so use the local value 'world' 96} 97``` 98 99## Constraints 1001. \@Provider and \@Consumer are property decorators for custom components. They can only modify the attributes of custom components and cannot modify the class attributes. 1012. \@Provider and \@Consumer are new state management decorators, which can be used only in \@ComponentV2 but not in \@Component. 102 103## Use Scenarios 104 105### Synchronizing \@Provider and \@Consumer in a Two-Way Manner 106#### Establish a Two-Way Binding 1071. Initialize the **Parent** and **Child** custom components: 108 - **@Consumer() str: string = 'world'** in the **Child** component searches upwards to find **@Provider() str: string = 'hello'** in the **Parent** component. 109 - **@Consumer() str: string = 'world'** is initialized to the value of **@Provider**, that is, **'hello'**. 110 - Both of them establish a two-way synchronization relationship. 1112. Click the button in the **Parent** component to change the @Provider decorated **str** and notify the corresponding @Consumer. UI will be updated. 1123. Click the button in the **Child** component to change the @Consumer decorated **str** and notify the corresponding @Provider. UI will be updated. 113 114```ts 115@Entry 116@ComponentV2 117struct Parent { 118 @Provider() str: string = 'hello'; 119 120 build() { 121 Column() { 122 Button(this.str) 123 .onClick(() => { 124 this.str += '0'; 125 }) 126 Child() 127 } 128 } 129} 130 131 132@ComponentV2 133struct Child { 134 @Consumer() str: string = 'world'; 135 136 build() { 137 Column() { 138 Button(this.str) 139 .onClick(() => { 140 this.str += '0'; 141 }) 142 } 143 } 144} 145``` 146#### Fail to Establish a Two-Way Binding 147 148In the following example, \@Provider and \@Consumer fail to establish a two-way synchronization relationship because of different **key** value. 1491. Initialize the **Parent** and **Child** custom components: 150 - @Provider is not found when **@Consumer() str: string = 'world'** in the **Child** component searches upwards. 151 - **@Consumer() str: string = 'world'** uses the local default value 'world'. 152 - Both of them fail to establish a two-way synchronization relationship. 1532. Click the button in the **Parent** component to change @Provider decorated **str1** and refresh the **Button** component associated with @Provider. 1543. Click the button in the **Child** component to change the @Consumer decorated **str** and refresh the **Button** component associated with @Consumer. 155 156```ts 157@Entry 158@ComponentV2 159struct Parent { 160 @Provider() str1: string = 'hello'; 161 162 build() { 163 Column() { 164 Button(this.str1) 165 .onClick(() => { 166 this.str1 += '0'; 167 }) 168 Child() 169 } 170 } 171} 172 173 174@ComponentV2 175struct Child { 176 @Consumer() str: string = 'world'; 177 178 build() { 179 Column() { 180 Button(this.str) 181 .onClick(() => { 182 this.str += '0'; 183 }) 184 } 185 } 186} 187``` 188 189### Decorating Callback by Using \@Provider and \@Consumer and Facilitating Behavior Abstraction Between Components 190 191To register a callback function for a child component in a parent component, you can use \@Provider and \@Consumer to decorate a callback. 192For instance, when a drag event occurs, if you want to synchronize the start position of a child component to the parent component, see the following example: 193 194```ts 195@Entry 196@ComponentV2 197struct Parent { 198 @Local childX: number = 0; 199 @Local childY: number = 1; 200 @Provider() onDrag: (x: number, y: number) => void = (x: number, y: number) => { 201 console.log(`onDrag event at x=${x} y:${y}`); 202 this.childX = x; 203 this.childY = y; 204 } 205 206 build() { 207 Column() { 208 Text(`child position x: ${this.childX}, y: ${this.childY}`) 209 Child() 210 } 211 } 212} 213 214@ComponentV2 215struct Child { 216 @Consumer() onDrag: (x: number, y: number) => void = (x: number, y: number) => {}; 217 218 build() { 219 Button("changed") 220 .draggable(true) 221 .onDragStart((event: DragEvent) => { 222 // Current previewer does not support common drag events. 223 this.onDrag(event.getDisplayX(), event.getDisplayY()); 224 }) 225 } 226} 227``` 228 229 230### Decorating Complex Types by Using \@Provider, \@Consumer, and \@Trace 231 2321. \@Provider and \@Consumer can only observe the changes of the data. If they are used to decorate complex data types and you need to observe the changes of the attributes, \@Trace is also required. 2332. Changes of some APIs can be observed when modifying **buildin** types, such as Array, Map, Set, and Data. The observation capability is the same as that of [\@Trace](./arkts-new-observedV2-and-trace.md#observed-changes). 234 235```ts 236@ObservedV2 237class User { 238 @Trace name: string; 239 @Trace age: number; 240 241 constructor(name: string, age: number) { 242 this.name = name; 243 this.age = age; 244 } 245} 246 247const data: User[] = [new User('Json', 10), new User('Eric', 15)]; 248 249@Entry 250@ComponentV2 251struct Parent { 252 @Provider('data') users: User[] = data; 253 254 build() { 255 Column() { 256 Child() 257 Button('age new user') 258 .onClick(() => { 259 this.users.push(new User('Molly', 18)); 260 }) 261 Button('age++') 262 .onClick(() => { 263 this.users[0].age++; 264 }) 265 Button('change name') 266 .onClick(() => { 267 this.users[0].name = 'Shelly'; 268 }) 269 } 270 } 271} 272 273 274@ComponentV2 275struct Child { 276 @Consumer('data') users: User[] = []; 277 278 build() { 279 Column() { 280 ForEach(this.users, (item: User) => { 281 Column() { 282 Text(`name: ${item.name}`).fontSize(30) 283 Text(`age: ${item.age}`).fontSize(30) 284 Divider() 285 } 286 }) 287 } 288 } 289} 290``` 291 292### Searching Upwards by \@Consumer for the Nearest \@Provider 293If \@Provider has duplicate names in the component tree, \@Consumer will search upwards for the \@Provider data of the nearest parent node. 294- In **AComp**, \@Consumer searches upwards to find **@Provider() val: number = 10** defined in the **Parent** component. Therefore, the value is initialized to 10. 295- In **A1Comp**, \@Consumer searches upwards to find **@Provider() val: number = 20** defined in **AComp** and stops searching when it is found. Therefore, the value is initialized to 20. 296 297```ts 298@Entry 299@ComponentV2 300struct Parent { 301 @Provider() val: number = 10; 302 303 build() { 304 Column() { 305 AComp() 306 } 307 308 } 309} 310 311@ComponentV2 312struct AComp { 313 @Provider() val: number = 20; 314 @Consumer("val") val2: number = 0; // 10 315 316 build() { 317 Column() { 318 Text(`${this.val2}`) 319 A1Comp() 320 } 321 322 } 323} 324 325@ComponentV2 326struct A1Comp { 327 @Consumer() val: number = 0; // 20 328 329 build() { 330 Text(`${this.val}`) 331 } 332} 333``` 334 335### Initializing \@Param by \@Provider and \@Consumer 336- Click **Text** (**@Consumer val: ${this.val}**) to trigger the change of **@Consumer() val**. This change will be synchronized to **@Provider() val** in the **Parent** component, triggering the change of the **Text** (**@Param val2: ${this.val2}**) in the **Child** component. 337- The change of **@Consumer() val** is also synchronized to **A1Comp**, triggering the change of **Text** (**A1Comp @Param val ${this.val}**). 338 339```ts 340@Entry 341@ComponentV2 342struct Parent { 343 @Provider() val: number = 10; 344 345 build() { 346 Column() { 347 AComp({ val2: this.val }) 348 } 349 } 350} 351 352@ComponentV2 353struct AComp { 354 @Consumer() val: number = 0; 355 @Param val2: number = 0; 356 357 build() { 358 Column() { 359 Text(`AComp @Consumer val: ${this.val}`).fontSize(30).onClick(() => { 360 this.val++; 361 }) 362 Text(`AComp @Param val2: ${this.val2}`).fontSize(30) 363 A1Comp({ val: this.val }) 364 }.border({ width: 2, color: Color.Green }) 365 } 366} 367 368@ComponentV2 369struct A1Comp { 370 @Param val: number = 0; 371 372 build() { 373 Column() { 374 Text(`A1Comp @Param val ${this.val}`).fontSize(30) 375 }.border({ width: 2, color: Color.Pink }) 376 } 377} 378``` 379 380<!--no_check-->