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 input 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 | No. To change the value, use the [\@Event](./arkts-new-event.md) decorator. | 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](./arkts-require.md) decorator and initialization must be passed 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 external 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, use 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 // If the @Local decorated variable of the parent component is changed, the @Param decorated variable is synchronized to the child component. 426 this.info = new Info("Lucy"); 427 }) 428 Child({ info: this.info }) 429 } 430 } 431 } 432 @ComponentV2 433 struct Child { 434 @Require @Param info: Info; 435 build() { 436 Column() { 437 Text(`info.name: ${this.info.name}`) 438 Button("change info") 439 .onClick(() => { 440 // Incorrect usage. The @Param decorated variable cannot be changed in the child component. An error is reported during compilation. 441 this.info = new Info("Jack"); 442 }) 443 Button("Child change info.name") 444 .onClick(() => { 445 // The properties of an object can be changed in the child component and this change is synchronized to the data source of the parent component. When the properties are decorated by @Trace, the corresponding UI re-rendering is observable. 446 this.info.name = "Jack"; 447 }) 448 } 449 } 450 } 451 ``` 452 453## Use Scenarios 454 455### Passing and Synchronizing Variables from the Parent Component to the Child Component 456 457\@Param receives and synchronizes the data passed in by the \@Local or \@Param parent component in real time. 458 459```ts 460@ObservedV2 461class Region { 462 @Trace x: number; 463 @Trace y: number; 464 constructor(x: number, y: number) { 465 this.x = x; 466 this.y = y; 467 } 468} 469@ObservedV2 470class Info { 471 @Trace name: string; 472 @Trace age: number; 473 @Trace region: Region; 474 constructor(name: string, age: number, x: number, y: number) { 475 this.name = name; 476 this.age = age; 477 this.region = new Region(x, y); 478 } 479} 480@Entry 481@ComponentV2 482struct Index { 483 @Local infoList: Info[] = [new Info("Alice", 8, 0, 0), new Info("Barry", 10, 1, 20), new Info("Cindy", 18, 24, 40)]; 484 build() { 485 Column() { 486 ForEach(this.infoList, (info: Info) => { 487 MiddleComponent({ info: info }) 488 }) 489 Button("change") 490 .onClick(() => { 491 this.infoList[0] = new Info("Atom", 40, 27, 90); 492 this.infoList[1].name = "Bob"; 493 this.infoList[2].region = new Region(7, 9); 494 }) 495 } 496 } 497} 498@ComponentV2 499struct MiddleComponent { 500 @Require @Param info: Info; 501 build() { 502 Column() { 503 Text(`name: ${this.info.name}`) 504 Text(`age: ${this.info.age}`) 505 SubComponent({ region: this.info.region }) 506 } 507 } 508} 509@ComponentV2 510struct SubComponent { 511 @Require @Param region: Region; 512 build() { 513 Column() { 514 Text(`region: ${this.region.x}-${this.region.y}`) 515 } 516 } 517} 518``` 519 520### Decorating Variables of the Date Type 521 522By 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**. 523 524```ts 525@ComponentV2 526struct DateComponent { 527 @Param selectedDate: Date = new Date('2024-01-01'); 528 529 build() { 530 Column() { 531 DatePicker({ 532 start: new Date('1970-1-1'), 533 end: new Date('2100-1-1'), 534 selected: this.selectedDate 535 }) 536 } 537 } 538} 539 540@Entry 541@ComponentV2 542struct ParentComponent { 543 @Local parentSelectedDate: Date = new Date('2021-08-08'); 544 545 build() { 546 Column() { 547 Button('parent update the new date') 548 .margin(10) 549 .onClick(() => { 550 this.parentSelectedDate = new Date('2023-07-07') 551 }) 552 Button('increase the year by 1') 553 .margin(10) 554 .onClick(() => { 555 this.parentSelectedDate.setFullYear(this.parentSelectedDate.getFullYear() + 1) 556 }) 557 Button('increase the month by 1') 558 .margin(10) 559 .onClick(() => { 560 this.parentSelectedDate.setMonth(this.parentSelectedDate.getMonth() + 1) 561 }) 562 Button('parent increase the day by 1') 563 .margin(10) 564 .onClick(() => { 565 this.parentSelectedDate.setDate(this.parentSelectedDate.getDate() + 1) 566 }) 567 DateComponent({ selectedDate: this.parentSelectedDate }) 568 } 569 } 570} 571``` 572 573### Decorating Variables of the Map Type 574 575By 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. 576 577```ts 578@ComponentV2 579struct Child { 580 @Param value: Map<number, string> = new Map() 581 582 build() { 583 Column() { 584 ForEach(Array.from(this.value.entries()), (item: [number, string]) => { 585 Text(`${item[0]}`).fontSize(30) 586 Text(`${item[1]}`).fontSize(30) 587 Divider() 588 }) 589 } 590 } 591} 592@Entry 593@ComponentV2 594struct MapSample2 { 595 @Local message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]]) 596 597 build() { 598 Row() { 599 Column() { 600 Child({ value: this.message }) 601 Button('init map').onClick(() => { 602 this.message = new Map([[0, "a"], [1, "b"], [3, "c"]]) 603 }) 604 Button('set new one').onClick(() => { 605 this.message.set(4, "d") 606 }) 607 Button('clear').onClick(() => { 608 this.message.clear() 609 }) 610 Button('replace the first one').onClick(() => { 611 this.message.set(0, "aa") 612 }) 613 Button('delete the first one').onClick(() => { 614 this.message.delete(0) 615 }) 616 } 617 .width('100%') 618 } 619 .height('100%') 620 } 621} 622``` 623 624### Decorating Variables of the Set Type 625 626By 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. 627 628```ts 629@ComponentV2 630struct Child { 631 @Param message: Set<number> = new Set() 632 633 build() { 634 Column() { 635 ForEach(Array.from(this.message.entries()), (item: [number, string]) => { 636 Text(`${item[0]}`).fontSize(30) 637 Divider() 638 }) 639 } 640 .width('100%') 641 } 642} 643@Entry 644@ComponentV2 645struct SetSample11 { 646 @Local message: Set<number> = new Set([0, 1, 2, 3, 4]) 647 648 build() { 649 Row() { 650 Column() { 651 Child({ message: this.message }) 652 Button('init set').onClick(() => { 653 this.message = new Set([0, 1, 2, 3, 4]) 654 }) 655 Button('set new one').onClick(() => { 656 this.message.add(5) 657 }) 658 Button('clear').onClick(() => { 659 this.message.clear() 660 }) 661 Button('delete the first one').onClick(() => { 662 this.message.delete(0) 663 }) 664 } 665 .width('100%') 666 } 667 .height('100%') 668 } 669} 670``` 671 672### Union Type 673 674\@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. 675 676```ts 677@Entry 678@ComponentV2 679struct Index { 680 @Local count: number | undefined = 0; 681 682 build() { 683 Column() { 684 MyComponent({ count: this.count }) 685 Button('change') 686 .onClick(() => { 687 this.count = undefined; 688 }) 689 } 690 } 691} 692 693@ComponentV2 694struct MyComponent { 695 @Param count: number | undefined = 0; 696 697 build() { 698 Column() { 699 Text(`count(${this.count})`) 700 } 701 } 702} 703``` 704