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