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>State management V2 is still under development, and some features may be incomplete or not always work as expected.
11
12## Overview
13
14\@Param indicates the state passed in from the external, ensuring that data can be synchronized between the parent and child components.
15
16- Variables decorated by \@Param can be initialized locally, but cannot be changed within the component.
17
18- \@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.
19- \@Param can accept data sources of any type, including common variables, state variables, constants, and function return values.
20- When an \@Param decorated variable changes, the component associated with the variable will be re-rendered.
21- \@Param can observe basic types such as number, boolean, string, object, and class and built-in types such as Array, Set, Map, and Date.
22- 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.
23- \@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).
24- \@Param supports null, undefined, and union types.
25
26
27## Limitations of State Management V1 to Accept Decorators Passed in Externally
28The 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.
29
30```ts
31@Observed
32class Region {
33  x: number;
34  y: number;
35  constructor(x: number, y: number) {
36    this.x = x;
37    this.y = y;
38  }
39}
40@Observed
41class Info {
42  region: Region;
43  constructor(x: number, y: number) {
44    this.region = new Region(x, y);
45  }
46}
47@Entry
48@Component
49struct Index {
50  @State info: Info = new Info(0, 0);
51  
52  build() {
53    Column() {
54      Button("change Info")
55        .onClick(() => {
56          this.info = new Info(100, 100);
57      })
58      Child({
59        region: this.info.region,
60        regionProp: this.info.region,
61        infoProp: this.info,
62        infoLink: this.info,
63        infoState: this.info
64      })
65    }
66  }
67}
68@Component
69struct Child {
70  @ObjectLink region: Region;
71  @Prop regionProp: Region;
72  @Prop infoProp: Info;
73  @Link infoLink: Info;
74  @State infoState: Info = new Info(1, 1);
75  build() {
76    Column() {
77      Text(`ObjectLink region: ${this.region.x}-${this.region.y}`)
78      Text(`Prop regionProp: ${this.regionProp.x}-${this.regionProp.y}`)
79    }
80  }
81}
82```
83
84In 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.
85
86## Decorator Description
87
88| \@Param Variable Decorator | Description                                                        |
89| ------------------ | ------------------------------------------------------------ |
90| Parameter        | None.                                                        |
91| Allowed local modification      | None. The capability of the \@Event decorator is required to change the value.                       |
92| Synchronization type          | One-way synchronization from the parent to the child component.                                          |
93| 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.|
94| 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.|
95
96## Variable Passing
97
98| Passing Rules      | Description                                                        |
99| -------------- | ------------------------------------------------------------ |
100| 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.|
101| Child component initialization  | \@Param decorated variables can initialize themselves in the child components.      |
102| 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.|
103
104## Observed Changes
105
106\@Param decorated variables enjoys observation capability. When a decorated variable changes, the UI component bound to the variable will be re-rendered.
107
108- When the decorated variable type is boolean, string, or number, you can observe the changes synchronized from the data source.
109
110  ```ts
111  @Entry
112  @ComponentV2
113  struct Index {
114    @Local count: number = 0;
115    @Local message: string = "Hello";
116    @Local flag: boolean = false;
117    build() {
118      Column() {
119        Text(`Local ${this.count}`)
120        Text(`Local ${this.message}`)
121        Text(`Local ${this.flag}`)
122        Button("change Local")
123          .onClick(()=>{
124            // Changes to the data source will be synchronized to the child component.
125            this.count++;
126            this.message += " World";
127            this.flag = !this.flag;
128        })
129        Child({
130          count: this.count,
131          message: this.message,
132          flag: this.flag
133        })
134      }
135    }
136  }
137  @ComponentV2
138  struct Child {
139    @Require @Param count: number;
140    @Require @Param message: string;
141    @Require @Param flag: boolean;
142    build() {
143      Column() {
144        Text(`Param ${this.count}`)
145        Text(`Param ${this.message}`)
146        Text(`Param ${this.flag}`)
147      }
148    }
149  }
150  ```
151
152- 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.
153
154  ```ts
155  class RawObject {
156    name: string;
157    constructor(name: string) {
158      this.name = name;
159    }
160  }
161  @ObservedV2
162  class ObservedObject {
163    @Trace name: string;
164    constructor(name: string) {
165      this.name = name;
166    }
167  }
168  @Entry
169  @ComponentV2
170  struct Index {
171    @Local rawObject: RawObject = new RawObject("rawObject");
172    @Local observedObject: ObservedObject = new ObservedObject("observedObject");
173    build() {
174      Column() {
175        Text(`${this.rawObject.name}`)
176        Text(`${this.observedObject.name}`)
177        Button("change object")
178          .onClick(() => {
179            // Overall changes to the class object can be observed.
180            this.rawObject = new RawObject("new rawObject");
181            this.observedObject = new ObservedObject("new observedObject");
182        })
183        Button("change name")
184          .onClick(() => {
185            // \@Local and \@Param cannot observe the class object properties. Therefore, the changes of rawObject.name cannot be observed.
186            this.rawObject.name = "new rawObject name";
187            // The name property of ObservedObject is decorated by @Trace. Therefore, the changes of observedObject.name can be observed.
188            this.observedObject.name = "new observedObject name";
189        })
190        Child({
191          rawObject: this.rawObject,
192          observedObject: this.observedObject
193        })
194      }
195    }
196  }
197  @ComponentV2
198  struct Child {
199    @Require @Param rawObject: RawObject;
200    @Require @Param observedObject: ObservedObject;
201    build() {
202      Column() {
203        Text(`${this.rawObject.name}`)
204        Text(`${this.observedObject.name}`)
205      }
206    }
207    
208  }
209  ```
210
211- When the decorated variable type is a simple array, you can observe the changes of the entire array or the array items.
212
213  ```ts
214  @Entry
215  @ComponentV2
216  struct Index {
217    @Local numArr: number[] = [1,2,3,4,5];
218    @Local dimensionTwo: number[][] = [[1,2,3],[4,5,6]];
219    
220    build() {
221      Column() {
222        Text(`${this.numArr[0]}`)
223        Text(`${this.numArr[1]}`)
224        Text(`${this.numArr[2]}`)
225        Text(`${this.dimensionTwo[0][0]}`)
226        Text(`${this.dimensionTwo[1][1]}`)
227        Button("change array item")
228          .onClick(() => {
229            this.numArr[0]++;
230            this.numArr[1] += 2;
231            this.dimensionTwo[0][0] = 0;
232            this.dimensionTwo[1][1] = 0;
233          })
234        Button("change whole array")
235          .onClick(() => {
236            this.numArr = [5,4,3,2,1];
237            this.dimensionTwo = [[7,8,9],[0,1,2]];
238          })
239        Child({
240          numArr: this.numArr,
241          dimensionTwo: this.dimensionTwo
242        })
243      }
244    }
245  }
246  @ComponentV2
247  struct Child {
248    @Require @Param numArr: number[];
249    @Require @Param dimensionTwo: number[][];
250    
251    build() {
252      Column() {
253        Text(`${this.numArr[0]}`)
254        Text(`${this.numArr[1]}`)
255        Text(`${this.numArr[2]}`)
256        Text(`${this.dimensionTwo[0][0]}`)
257        Text(`${this.dimensionTwo[1][1]}`)
258      }
259    }
260  }
261  ```
262
263- 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.
264
265  ```ts
266  @ObservedV2
267  class Region {
268    @Trace x: number;
269    @Trace y: number;
270    constructor(x: number, y: number) {
271      this.x = x;
272      this.y = y;
273    }
274  }
275  @ObservedV2
276  class Info {
277    @Trace region: Region;
278    @Trace name: string;
279    constructor(name: string, x: number, y: number) {
280      this.name = name;
281      this.region = new Region(x, y);
282    }
283  }
284  @Entry
285  @ComponentV2
286  struct Index {
287    @Local infoArr: Info[] = [new Info("Ocean", 28, 120), new Info("Mountain", 26, 20)];
288    @Local originInfo: Info = new Info("Origin", 0, 0);
289    build() {
290      Column() {
291        ForEach(this.infoArr, (info: Info) => {
292          Row() {
293            Text(`name: ${info.name}`)
294            Text(`region: ${info.region.x}-${info.region.y}`)
295          }
296        })
297        Row() {
298          Text(`Origin name: ${this.originInfo.name}`)
299          Text(`Origin region: ${this.originInfo.region.x}-${this.originInfo.region.y}`)
300        }
301        Button("change infoArr item")
302          .onClick(() => {
303            // Because the name property is decorated by @Trace, it can be observed.
304            this.infoArr[0].name = "Win";
305          })
306        Button("change originInfo")
307          .onClick(() => {
308            // Because the variable originInfo is decorated by @Local, it can be observed.
309            this.originInfo = new Info("Origin", 100, 100);
310          })
311        Button("change originInfo region")
312          .onClick(() => {
313            // Because the x and y properties are decorated by @Trace, it can be observed.
314            this.originInfo.region.x = 25;
315            this.originInfo.region.y = 25;
316          })
317      }
318    }
319  }
320  @ComponentV2
321  struct Child {
322    @Param infoArr: Info[] = [];
323    @Param originInfo: Info = new Info("O", 0, 0);
324  
325    build() {
326      Column() {
327        ForEach(this.infoArr, (info: Info) => {
328          Row() {
329            Text(`name: ${info.name}`)
330            Text(`region: ${info.region.x}-${info.region.y}`)
331          }
332        })
333        Row() {
334          Text(`Origin name: ${this.originInfo.name}`)
335          Text(`Origin region: ${this.originInfo.region.x}-${this.originInfo.region.y}`)
336        }
337      }
338    }
339  }
340  ```
341
342- 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.
343
344  | Type | Observable APIs                                             |
345  | ----- | ------------------------------------------------------------ |
346  | Array | push, pop, shift, unshift, splice, copyWithin, fill, reverse, sort|
347  | Date  | setFullYear, setMonth, setDate, setHours, setMinutes, setSeconds, setMilliseconds, setTime, setUTCFullYear, setUTCMonth, setUTCDate, setUTCHours, setUTCMinutes, setUTCSeconds, setUTCMilliseconds |
348  | Map   | set, clear, delete                                           |
349  | Set   | add, clear, delete                                           |
350
351## Constraints
352
353The \@Param decorator has the following constraints:
354
355- The \@Param decorator can be used only in custom components decorated by \@ComponentV2.
356
357  ```ts
358  @ComponentV2
359  struct CompA {
360    @Param message: string = "Hello World"; // Correct usage.
361    build() {
362    }
363  }
364  @Component
365  struct CompB {
366    @Param message: string = "Hello World"; // Incorrect usage.
367    build() {
368    }
369  }
370  ```
371
372- 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.
373
374  ```ts
375  @ComponentV2
376  struct CompA {
377    @Param param1: string = "Initialize local";
378    @Param param2: string = "Initialize local and put in";
379    @Require @Param param3: string;
380    build() {
381      Column() {
382        Text(`${this.param1}`) // Initialize local
383        Text(`${this.param2}`) // Put in
384        Text(`${this.param3}`) // Put in
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- \@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 variables cannot be changed in the child component.
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