• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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