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