1# \@Param Decorator: Inputting External Parameters to Components 2 3 4You can use \@Param, a variable decorator in state management V2, to enhance the capability of child components to receive external parameter input. 5 6> **NOTE** 7> 8> The \@Param decorator is supported since API version 12. 9> 10 11## Overview 12 13\@Param indicates the state passed in from the external, ensuring that data can be synchronized between the parent and child components. 14 15- Variables decorated by \@Param can be initialized locally, but cannot be changed within the component. 16 17- \@Param decorated variables can be passed in from the external when initializing a custom component. When the data source is also a state variable, changes of the data source will be synchronized to \@Param. 18- \@Param can accept data sources of any type, including common variables, state variables, constants, and function return values. 19- When an \@Param decorated variable changes, the component associated with the variable will be re-rendered. 20- \@Param can observe basic types such as number, boolean, string, object, and class and built-in types such as Array, Set, Map, and Date. 21- For complex types such as class objects, \@Param accepts references from the data source. You can change the class object properties in the component and this change will be synchronized to the data source. 22- \@Param can only observe the decorated variables. When a simple type is decorated, the overall change of variables can be observed. When an object type is decorated, only the overall change of the object can be observed. When an array type is decorated, the overall change of the entire array and its elements can be observed. When the built-in types such as Array, Set, Map, and Date are decorated, the changes brought by API invoking can be observed. For details, see [Observed Changes](#observed-changes). 23- \@Param supports null, undefined, and union types. 24 25 26## Limitations of State Management V1 to Accept Decorators Passed in Externally 27The state management V1 has multiple decorators that can be passed in from external systems. The common-used decorators are \@State, \@Prop, \@Link, and \@ObjectLink. These decorators have their own constraints and are difficult to be distinguished. When they are used improperly, they may cause performance problems. 28 29```ts 30@Observed 31class Region { 32 x: number; 33 y: number; 34 constructor(x: number, y: number) { 35 this.x = x; 36 this.y = y; 37 } 38} 39@Observed 40class Info { 41 region: Region; 42 constructor(x: number, y: number) { 43 this.region = new Region(x, y); 44 } 45} 46@Entry 47@Component 48struct Index { 49 @State info: Info = new Info(0, 0); 50 51 build() { 52 Column() { 53 Button("change Info") 54 .onClick(() => { 55 this.info = new Info(100, 100); 56 }) 57 Child({ 58 region: this.info.region, 59 regionProp: this.info.region, 60 infoProp: this.info, 61 infoLink: this.info, 62 infoState: this.info 63 }) 64 } 65 } 66} 67@Component 68struct Child { 69 @ObjectLink region: Region; 70 @Prop regionProp: Region; 71 @Prop infoProp: Info; 72 @Link infoLink: Info; 73 @State infoState: Info = new Info(1, 1); 74 build() { 75 Column() { 76 Text(`ObjectLink region: ${this.region.x}-${this.region.y}`) 77 Text(`Prop regionProp: ${this.regionProp.x}-${this.regionProp.y}`) 78 } 79 } 80} 81``` 82 83In the preceding example, \@State can obtain the reference of **info** only during initialization. After **info** is changed, synchronization cannot be performed. \@Prop supports one-way synchronization, but the deep copy performance is still poor for complex types. \@Link can synchronize the input reference in a two-way manner, but it requires that the data source be also a state variable. Therefore, it cannot accept the member property **region** in **info**. \@ObjectLink can accept the class member property which must be decorated by \@Observed. Different constraints of the decorator make the rules for transferring values between parent and child components complex and difficult to use. This is where \@Param, a decorator that indicates the component state passed in from the external, comes into the picture. 84 85## Decorator Description 86 87| \@Param Variable Decorator | Description | 88| ------------------ | ------------------------------------------------------------ | 89| Parameter | None. | 90| Allowed local modification | None. The capability of the \@Event decorator is required to change the value. | 91| Synchronization type | One-way synchronization from the parent to the child component. | 92| Allowed variable types| Basic types such as object, class, string, number, boolean, and enum and embedded types such as Array, Date, Map, and Set. Null, undefined, and union types are supported.| 93| Initial value for the decorated variable| Local initialization is allowed. If local initialization is not performed, this parameter must be used together with the \@Require decorator and initialization must be passed in from the external.| 94 95## Variable Passing 96 97| Passing Rules | Description | 98| -------------- | ------------------------------------------------------------ | 99| Initialization from the parent component| \@Param decorated variables can be initialized locally. If local initialization does not performed, the variables must be initialized from the external. When both the local initial value and externally input value exist, the latter is preferentially used for initialization.| 100| Child component initialization | \@Param decorated variables can initialize themselves in the child components. | 101| Synchronization | \@Param can be synchronized with the state variable data source passed in by the parent component (that is, the variable decorated by \@Local or \@Param). When the data source changes, the changes will be synchronized to \@Param of the child component.| 102 103## Observed Changes 104 105\@Param decorated variables enjoys observation capability. When a decorated variable changes, the UI component bound to the variable will be re-rendered. 106 107- When the decorated variable type is boolean, string, or number, you can observe the changes synchronized from the data source. 108 109 ```ts 110 @Entry 111 @ComponentV2 112 struct Index { 113 @Local count: number = 0; 114 @Local message: string = "Hello"; 115 @Local flag: boolean = false; 116 build() { 117 Column() { 118 Text(`Local ${this.count}`) 119 Text(`Local ${this.message}`) 120 Text(`Local ${this.flag}`) 121 Button("change Local") 122 .onClick(()=>{ 123 // Changes to the data source will be synchronized to the child component. 124 this.count++; 125 this.message += " World"; 126 this.flag = !this.flag; 127 }) 128 Child({ 129 count: this.count, 130 message: this.message, 131 flag: this.flag 132 }) 133 } 134 } 135 } 136 @ComponentV2 137 struct Child { 138 @Require @Param count: number; 139 @Require @Param message: string; 140 @Require @Param flag: boolean; 141 build() { 142 Column() { 143 Text(`Param ${this.count}`) 144 Text(`Param ${this.message}`) 145 Text(`Param ${this.flag}`) 146 } 147 } 148 } 149 ``` 150 151- When the decorated variable is of a class object type, only the overall value changes to the class object can be observed. To observe value changes to the member properties in the class object, you'll need the \@ObservedV2 and \@Trace decorators. 152 153 ```ts 154 class RawObject { 155 name: string; 156 constructor(name: string) { 157 this.name = name; 158 } 159 } 160 @ObservedV2 161 class ObservedObject { 162 @Trace name: string; 163 constructor(name: string) { 164 this.name = name; 165 } 166 } 167 @Entry 168 @ComponentV2 169 struct Index { 170 @Local rawObject: RawObject = new RawObject("rawObject"); 171 @Local observedObject: ObservedObject = new ObservedObject("observedObject"); 172 build() { 173 Column() { 174 Text(`${this.rawObject.name}`) 175 Text(`${this.observedObject.name}`) 176 Button("change object") 177 .onClick(() => { 178 // Overall changes to the class object can be observed. 179 this.rawObject = new RawObject("new rawObject"); 180 this.observedObject = new ObservedObject("new observedObject"); 181 }) 182 Button("change name") 183 .onClick(() => { 184 // \@Local and \@Param cannot observe the class object properties. Therefore, the changes of rawObject.name cannot be observed. 185 this.rawObject.name = "new rawObject name"; 186 // The name property of ObservedObject is decorated by @Trace. Therefore, the changes of observedObject.name can be observed. 187 this.observedObject.name = "new observedObject name"; 188 }) 189 Child({ 190 rawObject: this.rawObject, 191 observedObject: this.observedObject 192 }) 193 } 194 } 195 } 196 @ComponentV2 197 struct Child { 198 @Require @Param rawObject: RawObject; 199 @Require @Param observedObject: ObservedObject; 200 build() { 201 Column() { 202 Text(`${this.rawObject.name}`) 203 Text(`${this.observedObject.name}`) 204 } 205 } 206 207 } 208 ``` 209 210- When the decorated variable type is a simple array, you can observe the changes of the entire array or the array items. 211 212 ```ts 213 @Entry 214 @ComponentV2 215 struct Index { 216 @Local numArr: number[] = [1,2,3,4,5]; 217 @Local dimensionTwo: number[][] = [[1,2,3],[4,5,6]]; 218 219 build() { 220 Column() { 221 Text(`${this.numArr[0]}`) 222 Text(`${this.numArr[1]}`) 223 Text(`${this.numArr[2]}`) 224 Text(`${this.dimensionTwo[0][0]}`) 225 Text(`${this.dimensionTwo[1][1]}`) 226 Button("change array item") 227 .onClick(() => { 228 this.numArr[0]++; 229 this.numArr[1] += 2; 230 this.dimensionTwo[0][0] = 0; 231 this.dimensionTwo[1][1] = 0; 232 }) 233 Button("change whole array") 234 .onClick(() => { 235 this.numArr = [5,4,3,2,1]; 236 this.dimensionTwo = [[7,8,9],[0,1,2]]; 237 }) 238 Child({ 239 numArr: this.numArr, 240 dimensionTwo: this.dimensionTwo 241 }) 242 } 243 } 244 } 245 @ComponentV2 246 struct Child { 247 @Require @Param numArr: number[]; 248 @Require @Param dimensionTwo: number[][]; 249 250 build() { 251 Column() { 252 Text(`${this.numArr[0]}`) 253 Text(`${this.numArr[1]}`) 254 Text(`${this.numArr[2]}`) 255 Text(`${this.dimensionTwo[0][0]}`) 256 Text(`${this.dimensionTwo[1][1]}`) 257 } 258 } 259 } 260 ``` 261 262- When the decorated variable is of a nested class or is an object array, \@Param cannot observe the change of lower-level object attributes. Observation of lower-level object attributes requires the use of \@ObservedV2 and \@Trace decorators. 263 264 ```ts 265 @ObservedV2 266 class Region { 267 @Trace x: number; 268 @Trace y: number; 269 constructor(x: number, y: number) { 270 this.x = x; 271 this.y = y; 272 } 273 } 274 @ObservedV2 275 class Info { 276 @Trace region: Region; 277 @Trace name: string; 278 constructor(name: string, x: number, y: number) { 279 this.name = name; 280 this.region = new Region(x, y); 281 } 282 } 283 @Entry 284 @ComponentV2 285 struct Index { 286 @Local infoArr: Info[] = [new Info("Ocean", 28, 120), new Info("Mountain", 26, 20)]; 287 @Local originInfo: Info = new Info("Origin", 0, 0); 288 build() { 289 Column() { 290 ForEach(this.infoArr, (info: Info) => { 291 Row() { 292 Text(`name: ${info.name}`) 293 Text(`region: ${info.region.x}-${info.region.y}`) 294 } 295 }) 296 Row() { 297 Text(`Origin name: ${this.originInfo.name}`) 298 Text(`Origin region: ${this.originInfo.region.x}-${this.originInfo.region.y}`) 299 } 300 Button("change infoArr item") 301 .onClick(() => { 302 // Because the name property is decorated by @Trace, it can be observed. 303 this.infoArr[0].name = "Win"; 304 }) 305 Button("change originInfo") 306 .onClick(() => { 307 // Because the variable originInfo is decorated by @Local, it can be observed. 308 this.originInfo = new Info("Origin", 100, 100); 309 }) 310 Button("change originInfo region") 311 .onClick(() => { 312 // Because the x and y properties are decorated by @Trace, it can be observed. 313 this.originInfo.region.x = 25; 314 this.originInfo.region.y = 25; 315 }) 316 } 317 } 318 } 319 @ComponentV2 320 struct Child { 321 @Param infoArr: Info[] = []; 322 @Param originInfo: Info = new Info("O", 0, 0); 323 324 build() { 325 Column() { 326 ForEach(this.infoArr, (info: Info) => { 327 Row() { 328 Text(`name: ${info.name}`) 329 Text(`region: ${info.region.x}-${info.region.y}`) 330 } 331 }) 332 Row() { 333 Text(`Origin name: ${this.originInfo.name}`) 334 Text(`Origin region: ${this.originInfo.region.x}-${this.originInfo.region.y}`) 335 } 336 } 337 } 338 } 339 ``` 340 341- When the decorated variable is of a built-in type, you can observe the overall value assignment of the variable and the changes caused by API invoking. 342 343 | Type | Observable APIs | 344 | ----- | ------------------------------------------------------------ | 345 | Array | push, pop, shift, unshift, splice, copyWithin, fill, reverse, sort| 346 | Date | setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds | 347 | Map | set, clear, delete | 348 | Set | add, clear, delete | 349 350## Constraints 351 352The \@Param decorator has the following constraints: 353 354- The \@Param decorator can be used only in custom components decorated by \@ComponentV2. 355 356 ```ts 357 @ComponentV2 358 struct CompA { 359 @Param message: string = "Hello World"; // Correct usage. 360 build() { 361 } 362 } 363 @Component 364 struct CompB { 365 @Param message: string = "Hello World"; // Incorrect usage. An error is reported during compilation. 366 build() { 367 } 368 } 369 ``` 370 371- The \@Param decorated variable indicates the external input of the component and needs to be initialized. The local initial value can be used for initialization. But if the external input value exists, it is preferentially used for initialization. It is not allowed to use neither the local initial value nor the external input value. 372 373 ```ts 374 @ComponentV2 375 struct CompA { 376 @Param param1: string = "Initialize local"; 377 @Param param2: string = "Initialize local and put in"; 378 @Require @Param param3: string; 379 @Param param4: string; // Incorrect usage. The external initialization is not performed and no initial value exists in the local host. As a result, an error is reported during compilation. 380 build() { 381 Column() { 382 Text(`${this.param1}`) // Local initialization. "Initialize local" is displayed. 383 Text(`${this.param2}`) // External initialization. "Put in" is displayed. 384 Text(`${this.param3}`) // External initialization. "Put in" is displayed. 385 } 386 } 387 } 388 @Entry 389 @ComponentV2 390 struct CompB { 391 @Local message: string = "Put in"; 392 build() { 393 Column() { 394 CompA({ 395 param2: this.message, 396 param3: this.message 397 }) 398 } 399 } 400 } 401 ``` 402 403- The \@Param decorated variables cannot be changed in the child component. However, when the decorated variable is of object type, changing the object properties in the child component is allowed. 404 405 ```ts 406 @ObservedV2 407 class Info { 408 @Trace name: string; 409 constructor(name: string) { 410 this.name = name; 411 } 412 } 413 @Entry 414 @ComponentV2 415 struct Index { 416 @Local info: Info = new Info("Tom"); 417 build() { 418 Column() { 419 Text(`Parent info.name ${this.info.name}`) 420 Button("Parent change info") 421 .onClick(() => { 422 this.info = new Info("Lucy"); // When the @Local variable is changed in the parent component, the @Param variable corresponding to the child component is synchronized. 423 }) 424 Child({ info: this.info }) 425 } 426 } 427 } 428 @ComponentV2 429 struct Child { 430 @Require @Param info: Info; 431 build() { 432 Column() { 433 Text(`info.name: ${this.info.name}`) 434 Button("change info") 435 .onClick(() => { 436 this.info = new Info("Jack"); // Incorrect usage. The @Param decorated variable cannot be changed in the child component. An error is reported during compilation. 437 }) 438 Button("Child change info.name") 439 .onClick(() => { 440 this.info.name = "Jack"; // Changing the object properties in the child component is allowed. 441 }) 442 } 443 } 444 } 445 ``` 446 447## Use Scenarios 448 449### Passing and Synchronizing Variables from the Parent Component to the Child Component 450 451\@Param receives and synchronizes the data passed in by the \@Local or \@Param parent component in real time. 452 453```ts 454@ObservedV2 455class Region { 456 @Trace x: number; 457 @Trace y: number; 458 constructor(x: number, y: number) { 459 this.x = x; 460 this.y = y; 461 } 462} 463@ObservedV2 464class Info { 465 @Trace name: string; 466 @Trace age: number; 467 @Trace region: Region; 468 constructor(name: string, age: number, x: number, y: number) { 469 this.name = name; 470 this.age = age; 471 this.region = new Region(x, y); 472 } 473} 474@Entry 475@ComponentV2 476struct Index { 477 @Local infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]; 478 build() { 479 Column() { 480 ForEach(this.infoList, (info: Info) => { 481 MiddleComponent({ info: info }) 482 }) 483 Button("change") 484 .onClick(() => { 485 this.infoList[0] = new Info("Atom", 40, 27, 90); 486 this.infoList[1].name = "Bob"; 487 this.infoList[2].region = new Region(7, 9); 488 }) 489 } 490 } 491} 492@ComponentV2 493struct MiddleComponent { 494 @Require @Param info: Info; 495 build() { 496 Column() { 497 Text(`name: ${this.info.name}`) 498 Text(`age: ${this.info.age}`) 499 SubComponent({ region: this.info.region }) 500 } 501 } 502} 503@ComponentV2 504struct SubComponent { 505 @Require @Param region: Region; 506 build() { 507 Column() { 508 Text(`region: ${this.region.x}-${this.region.y}`) 509 } 510 } 511} 512``` 513 514### Decorating Variables of the Date Type 515 516By using \@Param to decorate the variables of the Date type, you can observe the value changes to the entire **Date** and the changes brought by calling the **Date** APIs: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**. 517 518```ts 519@ComponentV2 520struct DateComponent { 521 @Param selectedDate: Date = new Date('2024-01-01'); 522 523 build() { 524 Column() { 525 DatePicker({ 526 start: new Date('1970-1-1'), 527 end: new Date('2100-1-1'), 528 selected: this.selectedDate 529 }) 530 } 531 } 532} 533 534@Entry 535@ComponentV2 536struct ParentComponent { 537 @Local parentSelectedDate: Date = new Date('2021-08-08'); 538 539 build() { 540 Column() { 541 Button('parent update the new date') 542 .margin(10) 543 .onClick(() => { 544 this.parentSelectedDate = new Date('2023-07-07') 545 }) 546 Button('increase the year by 1') 547 .margin(10) 548 .onClick(() => { 549 this.parentSelectedDate.setFullYear(this.parentSelectedDate.getFullYear() + 1) 550 }) 551 Button('increase the month by 1') 552 .margin(10) 553 .onClick(() => { 554 this.parentSelectedDate.setMonth(this.parentSelectedDate.getMonth() + 1) 555 }) 556 Button('parent increase the day by 1') 557 .margin(10) 558 .onClick(() => { 559 this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1) 560 }) 561 DateComponent({ selectedDate: this.parentSelectedDate }) 562 } 563 } 564} 565``` 566 567### Decorating Variables of the Map Type 568 569By using \@Param to decorate the variables of the **Map** type, you can observe the overall value changes to the entire **Map** and the changes brought by calling the **Map** APIs: set, clear, and delete. 570 571```ts 572@ComponentV2 573struct Child { 574 @Param value: Map<number, string> = new Map() 575 576 build() { 577 Column() { 578 ForEach(Array.from(this.value.entries()), (item: [number, string]) => { 579 Text(`${item[0]}`).fontSize(30) 580 Text(`${item[1]}`).fontSize(30) 581 Divider() 582 }) 583 } 584 } 585} 586@Entry 587@ComponentV2 588struct MapSample2 { 589 @Local message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 590 591 build() { 592 Row() { 593 Column() { 594 Child({ value: this.message }) 595 Button('init map').onClick(() => { 596 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]) 597 }) 598 Button('set new one').onClick(() => { 599 this.message.set(4, "d") 600 }) 601 Button('clear').onClick(() => { 602 this.message.clear() 603 }) 604 Button('replace the first one').onClick(() => { 605 this.message.set(0, "aa") 606 }) 607 Button('delete the first one').onClick(() => { 608 this.message.delete(0) 609 }) 610 } 611 .width('100%') 612 } 613 .height('100%') 614 } 615} 616``` 617 618### Decorating Variables of the Set Type 619 620By using \@Param to decorate the variables of the **Set** type, you can observe the overall value changes to the entire **Set** and the changes brought by calling the **Set** APIs: add, clear, and delete. 621 622```ts 623@ComponentV2 624struct Child { 625 @Param message: Set<number> = new Set() 626 627 build() { 628 Column() { 629 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 630 Text(`${item[0]}`).fontSize(30) 631 Divider() 632 }) 633 } 634 .width('100%') 635 } 636} 637@Entry 638@ComponentV2 639struct SetSample11 { 640 @Local message: Set<number> = new Set([0, 1, 2, 3, 4]) 641 642 build() { 643 Row() { 644 Column() { 645 Child({ message: this.message }) 646 Button('init set').onClick(() => { 647 this.message = new Set([0, 1, 2, 3, 4]) 648 }) 649 Button('set new one').onClick(() => { 650 this.message.add(5) 651 }) 652 Button('clear').onClick(() => { 653 this.message.clear() 654 }) 655 Button('delete the first one').onClick(() => { 656 this.message.delete(0) 657 }) 658 } 659 .width('100%') 660 } 661 .height('100%') 662 } 663} 664``` 665 666### Union Type 667 668\@Param supports null, undefined, and union types. In the following example, the **count** type is of **number | undefined**. Click to change the **count** type, the UI will be re-rendered accordingly. 669 670```ts 671@Entry 672@ComponentV2 673struct Index { 674 @Local count: number | undefined = 0; 675 676 build() { 677 Column() { 678 MyComponent({ count: this.count }) 679 Button('change') 680 .onClick(() => { 681 this.count = undefined; 682 }) 683 } 684 } 685} 686 687@ComponentV2 688struct MyComponent { 689 @Param count: number | undefined = 0; 690 691 build() { 692 Column() { 693 Text(`count(${this.count})`) 694 } 695 } 696} 697``` 698