1# \@Link Decorator: Two-Way Synchronization Between Parent and Child Components
2
3
4An \@Link decorated variable creates two-way synchronization with a variable of its parent component.
5
6
7> **NOTE**
8>
9> This decorator can be used in ArkTS widgets since API version 9.
10>
11> This decorator can be used in atomic services since API version 11.
12
13## Overview
14
15An \@Link decorated variable in a child component shares the same value with a variable in its parent component.
16
17
18## Restrictions
19
20- The @Link decorator cannot be used in custom components decorated by [\@Entry](https://gitee.com/openharmony/docs/blob/master/en/application-dev/quick-start/arkts-create-custom-components.md#basic-structure-of-a-custom-component).
21
22
23## Rules of Use
24
25| \@Link Decorator                         | Description                                                  |
26| ---------------------------------------- | ------------------------------------------------------------ |
27| Decorator parameters                     | None.                                                        |
28| Synchronization type                     | Two-way:<br>from an \@State, \@StorageLink, or \@Link decorated variable in the parent component to this variable; and the other way around. |
29| Allowed variable types                   | Object, class, string, number, Boolean, enum, and array of these types.<br>Date type.<br>(Applicable to API version 11 or later) Map and Set types. <br>The union types defined by the ArkUI framework, including Length, ResourceStr, and ResourceColor.<br>The type must be specified and must be the same as that of the counterpart variable of the parent component.<br>For details about the scenarios of supported types, see [Observed Changes](#observed-changes).<br>**any** is not supported.<br>(Applicable to API version 11 and later versions) Union type of the preceding types, for example, **string \| number**, **string \| undefined** or **ClassA \| null**. For details, see [Union Type @Link](#union-type-link).<br>**NOTE**<br>When **undefined** or **null** is used, you are advised to explicitly specify the type to pass the TypeScript type check. For example, **@Link a: string \| undefined = undefined**. |
30| Initial value for the decorated variable | Forbidden.                                                   |
31
32
33## Variable Transfer/Access Rules
34
35| Transfer/Access     | Description                                      |
36| ---------- | ---------------------------------------- |
37| Initialization and update from the parent component| Mandatory. A two-way synchronization relationship can be established with the @State, @StorageLink, or \@Link decorated variable in the parent component. An @Link decorated variable can be initialized from an [\@State](./arkts-state.md), @Link, [\@Prop](./arkts-prop.md), [\@Provide](./arkts-provide-and-consume.md), [\@Consume](./arkts-provide-and-consume.md), [\@ObjectLink](./arkts-observed-and-objectlink.md), [\@StorageLink](./arkts-appstorage.md#storagelink), [\@StorageProp](./arkts-appstorage.md#storageprop), [\@LocalStorageLink](./arkts-localstorage.md#localstoragelink), or [\@LocalStorageProp](./arkts-localstorage.md#localstorageprop) decorated variable in the parent component.<br>Since API version 9, the syntax is **Comp({&nbsp;aLink:&nbsp;this.aState&nbsp;})** for initializing an \@Link decorated variable in the child component from an @State decorated variable in its parent component. The **Comp({aLink:&nbsp;$aState})** syntax is also supported.|
38| Child component initialization  | Supported; can be used to initialize a regular variable or \@State, \@Link, \@Prop, or \@Provide decorated variable in the child component.|
39| Access | Private, accessible only within the component.                          |
40
41  **Figure 1** Initialization rule 
42
43![en-us_image_0000001502092556](figures/en-us_image_0000001502092556.png)
44
45
46## Observed Changes and Behavior
47
48
49### Observed Changes
50
51- When the decorated variable is of the Boolean, string, or number type, its value change can be observed. For details, see [Example for @Link with Simple and Class Types](#example-for-link-with-simple-and-class-types).
52
53- When the decorated variable is of the class or Object type, its value change and value changes of all its attributes, that is, the attributes that **Object.keys(observedObject)** returns, can be observed. For details, see [Example for @Link with Simple and Class Types](#example-for-link-with-simple-and-class-types).
54
55- When the decorated variable is of the array type, the addition, deletion, and updates of array items can be observed. For details, see [Array Type \@Link](#array-type-link).
56
57- When the decorated variable is of the Date type, the overall value assignment of the Date object can be observed, and the following APIs can be called to update Date attributes: **setFullYear**, **setMonth**, **setDate**, **setHours**, **setMinutes**, **setSeconds**, **setMilliseconds**, **setTime**, **setUTCFullYear**, **setUTCMonth**, **setUTCDate**, **setUTCHours**, **setUTCMinutes**, **setUTCSeconds**, and **setUTCMilliseconds**.
58
59```ts
60@Component
61struct DateComponent {
62  @Link selectedDate: Date;
63
64  build() {
65    Column() {
66      Button(`child increase the year by 1`).onClick(() => {
67        this.selectedDate.setFullYear(this.selectedDate.getFullYear() + 1)
68      })
69      Button('child update the new date')
70        .margin(10)
71        .onClick(() => {
72          this.selectedDate = new Date('2023-09-09')
73        })
74      DatePicker({
75        start: new Date('1970-1-1'),
76        end: new Date('2100-1-1'),
77        selected: this.selectedDate
78      })
79    }
80
81  }
82}
83
84@Entry
85@Component
86struct ParentComponent {
87  @State parentSelectedDate: Date = new Date('2021-08-08');
88
89  build() {
90    Column() {
91      Button('parent increase the month by 1')
92        .margin(10)
93        .onClick(() => {
94          this.parentSelectedDate.setMonth(this.parentSelectedDate.getMonth() + 1)
95        })
96      Button('parent update the new date')
97        .margin(10)
98        .onClick(() => {
99          this.parentSelectedDate = new Date('2023-07-07')
100        })
101      DatePicker({
102        start: new Date('1970-1-1'),
103        end: new Date('2100-1-1'),
104        selected: this.parentSelectedDate
105      })
106
107      DateComponent({ selectedDate:this.parentSelectedDate })
108    }
109  }
110}
111```
112
113- When the decorated variable is **Map**, value changes of **Map** can be observed. In addition, you can call the **set**, **clear**, and **delete** APIs of **Map** to update its value. For details, see [Decorating Variables of the Map Type](#decorating-variables-of-the-map-type).
114
115- When the decorated variable is **Set**, value changes of **Set** can be observed. In addition, you can call the **add**, **clear**, and **delete** APIs of **Set** to update its value. For details, see [Decorating Variables of the Set Type](#decorating-variables-of-the-set-type).
116
117### Framework Behavior
118
119An \@Link decorated variable shares the lifecycle of its owning component.
120
121To understand the value initialization and update mechanism of the \@Link decorated variable, it is necessary to consider the parent component and the initial render and update process of the child component that owns the \@Link decorated variable (in this example, the \@State decorated variable in the parent component is used).
122
1231. Initial render: The execution of the parent component's **build()** function creates a new instance of the child component. The initialization process is as follows:
124   1. An \@State decorated variable of the parent component must be specified to initialize the child component's \@Link decorated variable. The child component's \@Link decorated variable value and its source variable are kept in sync (two-way data synchronization).
125   2. The \@State state variable wrapper class of the parent component is passed to the child component through the build function. After obtaining the \@State state variable of the parent component, the \@Link wrapper class of the child component registers the **this** pointer to the current \@Link wrapper class with the \@State variable of the parent component.
126
1272. Update of the \@Link source: When the state variable in the parent component is updated, the \@Link decorated variable in the related child component is updated. Processing steps:
128   1. As indicated in the initial rendering step, the child component's \@Link wrapper class registers the current **this** pointer with the parent component. When the \@State decorated variable in the parent component is changed, all system components (**elementid**) and state variables (such as the \@Link wrapper class) that depend on the parent component are traversed and updated.
129   2. After the \@Link wrapper class is updated, all system components (**elementId**) that depend on the \@Link decorated variable in the child component are notified of the update. In this way, the parent component has the state data of the child components synchronized.
130
1313. Update of \@Link: After the \@Link decorated variable in the child component is updated, the following steps are performed (the \@State decorated variable in the parent component is used):
132   1. After the \@Link decorated variable is updated, the **set** method of the \@State wrapper class in the parent component is called to synchronize the updated value back to the parent component.
133   2. The \@Link in the child component and \@State in the parent component traverse the dependent system components and update the corresponding UI. In this way, the \@Link decorated variable in the child component is synchronized back to the \@State decorated variable in the parent component.
134
135
136## Usage Scenarios
137
138
139### Example for @Link with Simple and Class Types
140
141In the following example, after **Parent View: Set yellowButton** and **Parent View: Set GreenButton** of the parent component **ShufflingContainer** are clicked, the change in the parent component is synchronized to the child components.
142
143  1. After buttons of the child components **GreenButton** and **YellowButton** are clicked, the child components (@Link decorated variables) change accordingly. Due to the two-way synchronization relationship between @Link and @State, the changes are synchronized to the parent component.
144  
145  2. When a button in the parent component **ShufflingContainer** is clicked, the parent component (@State decorated variable) changes, and the changes are synchronized to the child components, which are then updated accordingly.
146
147```ts
148class GreenButtonState {
149  width: number = 0;
150
151  constructor(width: number) {
152    this.width = width;
153  }
154}
155
156@Component
157struct GreenButton {
158  @Link greenButtonState: GreenButtonState;
159
160  build() {
161    Button('Green Button')
162      .width(this.greenButtonState.width)
163      .height(40)
164      .backgroundColor('#64bb5c')
165      .fontColor('#FFFFFF, 90%')
166      .onClick(() => {
167        if (this.greenButtonState.width < 700) {
168          // Update the attribute of the class. The change can be observed and synchronized back to the parent component.
169          this.greenButtonState.width += 60;
170        } else {
171          // Update the class. The change can be observed and synchronized back to the parent component.
172          this.greenButtonState = new GreenButtonState(180);
173        }
174      })
175  }
176}
177
178@Component
179struct YellowButton {
180  @Link yellowButtonState: number;
181
182  build() {
183    Button('Yellow Button')
184      .width(this.yellowButtonState)
185      .height(40)
186      .backgroundColor('#f7ce00')
187      .fontColor('#FFFFFF, 90%')
188      .onClick(() => {
189        // The change of the decorated variable of a simple type in the child component can be synchronized back to the parent component.
190        this.yellowButtonState += 40.0;
191      })
192  }
193}
194
195@Entry
196@Component
197struct ShufflingContainer {
198  @State greenButtonState: GreenButtonState = new GreenButtonState(180);
199  @State yellowButtonProp: number = 180;
200
201  build() {
202    Column() {
203      Flex({ direction: FlexDirection.Column, alignItems: ItemAlign.Center }) {
204        // Simple type @Link in the child component synchronized from @State in the parent component.
205        Button('Parent View: Set yellowButton')
206          .width(312)
207          .height(40)
208          .margin(12)
209          .fontColor('#FFFFFF, 90%')
210          .onClick(() => {
211            this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 40 : 100;
212          })
213        // Class type @Link in the child component synchronized from @State in the parent component.
214        Button('Parent View: Set GreenButton')
215          .width(312)
216          .height(40)
217          .margin(12)
218          .fontColor('#FFFFFF, 90%')
219          .onClick(() => {
220            this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
221          })
222        // Initialize the class type @Link.
223        GreenButton({ greenButtonState: $greenButtonState }).margin(12)
224        // Initialize the simple type @Link.
225        YellowButton({ yellowButtonState: $yellowButtonProp }).margin(12)
226      }
227    }
228  }
229}
230```
231
232![Video-link-UsageScenario-one](figures/Video-link-UsageScenario-one.gif)
233
234### Array Type \@Link
235
236
237```ts
238@Component
239struct Child {
240  @Link items: number[];
241
242  build() {
243    Column() {
244      Button(`Button1: push`)
245        .margin(12)
246        .width(312)
247        .height(40)
248        .fontColor('#FFFFFF, 90%')
249        .onClick(() => {
250          this.items.push(this.items.length + 1);
251        })
252      Button(`Button2: replace whole item`)
253        .margin(12)
254        .width(312)
255        .height(40)
256        .fontColor('#FFFFFF, 90%')
257        .onClick(() => {
258          this.items = [100, 200, 300];
259        })
260    }
261  }
262}
263
264@Entry
265@Component
266struct Parent {
267  @State arr: number[] = [1, 2, 3];
268
269  build() {
270    Column() {
271      Child({ items: $arr })
272        .margin(12)
273      ForEach(this.arr,
274        (item: number) => {
275          Button(`${item}`)
276            .margin(12)
277            .width(312)
278            .height(40)
279            .backgroundColor('#11a2a2a2')
280            .fontColor('#e6000000')
281        },
282        (item: ForEachInterface) => item.toString()
283      )
284    }
285  }
286}
287```
288
289![Video-link-UsageScenario-two](figures/Video-link-UsageScenario-two.gif)
290
291As described above, the ArkUI framework can observe the addition, deletion, and replacement of array items. It should be noted that, in the preceding example, the type of the \@Link and \@State decorated variables is the same: number[]. It is not allowed to define the \@Link decorated variable in the child component as type number (**\@Link item: number**), and create child components for each array item in the \@State decorated array in the parent component. [\@Prop](arkts-prop.md) or [\@Observed](./arkts-observed-and-objectlink.md) should be used depending on application semantics.
292
293### Decorating Variables of the Map Type
294
295> **NOTE**
296>
297> Since API version 11, \@Link supports the Map type.
298
299In this example, the **value** variable is of the Map<number, string> type. When the button is clicked, the value of **message** changes, and the UI is re-rendered.
300
301```ts
302@Component
303struct Child {
304  @Link value: Map<number, string>
305
306  build() {
307    Column() {
308      ForEach(Array.from(this.value.entries()), (item: [number, string]) => {
309        Text(`${item[0]}`).fontSize(30)
310        Text(`${item[1]}`).fontSize(30)
311        Divider()
312      })
313      Button('child init map').onClick(() => {
314        this.value = new Map([[0, "a"], [1, "b"], [3, "c"]])
315      })
316      Button('child set new one').onClick(() => {
317        this.value.set(4, "d")
318      })
319      Button('child clear').onClick(() => {
320        this.value.clear()
321      })
322      Button('child replace the first one').onClick(() => {
323        this.value.set(0, "aa")
324      })
325      Button('child delete the first one').onClick(() => {
326        this.value.delete(0)
327      })
328    }
329  }
330}
331
332
333@Entry
334@Component
335struct MapSample2 {
336  @State message: Map<number, string> = new Map([[0, "a"], [1, "b"], [3, "c"]])
337
338  build() {
339    Row() {
340      Column() {
341        Child({ value: this.message })
342      }
343      .width('100%')
344    }
345    .height('100%')
346  }
347}
348```
349
350### Decorating Variables of the Set Type
351
352> **NOTE**
353>
354> Since API version 11, \@Link supports the Set type.
355
356In this example, the **message** variable is of the Set\<number\> type. When the button is clicked, the value of **message** changes, and the UI is re-rendered.
357
358```ts
359@Component
360struct Child {
361  @Link message: Set<number>
362
363  build() {
364    Column() {
365      ForEach(Array.from(this.message.entries()), (item: [number, string]) => {
366        Text(`${item[0]}`).fontSize(30)
367        Divider()
368      })
369      Button('init set').onClick(() => {
370        this.message = new Set([0, 1, 2, 3, 4])
371      })
372      Button('set new one').onClick(() => {
373        this.message.add(5)
374      })
375      Button('clear').onClick(() => {
376        this.message.clear()
377      })
378      Button('delete the first one').onClick(() => {
379        this.message.delete(0)
380      })
381    }
382    .width('100%')
383  }
384}
385
386
387@Entry
388@Component
389struct SetSample1 {
390  @State message: Set<number> = new Set([0, 1, 2, 3, 4])
391
392  build() {
393    Row() {
394      Column() {
395        Child({ message: this.message })
396      }
397      .width('100%')
398    }
399    .height('100%')
400  }
401}
402```
403
404### Using Two-Way Synchronization Mechanism to Change Local Variables
405
406Use [\@Watch](./arkts-watch.md) to change local variables during two-way synchronization.
407
408In the following example, the \@State decorated variable **sourceNumber** is modified in \@Watch of \@Link to implement variable synchronization between parent and child components. However, the local modification of the \@State decorated variable **memberMessage** does not affect the variable change in the parent component.
409
410```ts
411@Entry
412@Component
413struct Parent {
414  @State sourceNumber: number = 0;
415
416  build() {
417    Column() {
418      Text(`sourceNumber of the parent component: ` + this.sourceNumber)
419      Child({ sourceNumber: this.sourceNumber })
420      Button('sourceNumber is changed in the parent component')
421        .onClick(() => {
422          this.sourceNumber++;
423        })
424    }
425    .width('100%')
426    .height('100%')
427  }
428}
429
430@Component
431struct Child {
432  @State memberMessage: string = 'Hello World';
433  @Link @Watch('onSourceChange') sourceNumber: number;
434
435  onSourceChange() {
436    this.memberMessage = this.sourceNumber.toString();
437  }
438
439  build() {
440    Column() {
441      Text(this.memberMessage)
442      Text(`sourceNumber of the child component: ` + this.sourceNumber.toString())
443      Button('memberMessage is changed in the child component')
444        .onClick(() => {
445          this.memberMessage = 'Hello memberMessage';
446        })
447    }
448  }
449}
450```
451
452## Union Type @Link
453
454@Link supports **undefined**, **null**, and union types. In the following example, the type of **name** is string | undefined. If the attribute or type of **name** is changed when the button in the parent component **Index** is clicked, the change will be synced to the child component.
455
456```ts
457@Component
458struct Child {
459  @Link name: string | undefined
460
461  build() {
462    Column() {
463
464      Button('Child change name to Bob')
465        .onClick(() => {
466          this.name = "Bob"
467        })
468
469      Button('Child change animal to undefined')
470        .onClick(() => {
471          this.name = undefined
472        })
473
474    }.width('100%')
475  }
476}
477
478@Entry
479@Component
480struct Index {
481  @State name: string | undefined = "mary"
482
483  build() {
484    Column() {
485      Text(`The name is  ${this.name}`).fontSize(30)
486
487      Child({ name: this.name })
488
489      Button('Parents change name to Peter')
490        .onClick(() => {
491          this.name = "Peter"
492        })
493
494      Button('Parents change name to undefined')
495        .onClick(() => {
496          this.name = undefined
497        })
498    }
499  }
500}
501```
502
503## FAQs
504
505### Incorrect Type of \@Link Decorated State Variable
506
507When using \@Link to decorate a state variable in a child component, ensure that the variable type is the same as the source type, and the source is a state variable decorated by a decorator such as \@State.
508
509[Negative example]
510
511```ts
512@Observed
513class ClassA {
514  public c: number = 0;
515
516  constructor(c: number) {
517    this.c = c;
518  }
519}
520
521@Component
522struct LinkChild {
523  @Link testNum: number;
524
525  build() {
526    Text(`LinkChild testNum ${this.testNum}`)
527  }
528}
529
530@Entry
531@Component
532struct Parent {
533  @State testNum: ClassA = new ClassA(1);
534
535  build() {
536    Column() {
537      Text(`Parent testNum ${this.testNum.c}`)
538        .onClick(() => {
539          this.testNum.c += 1;
540        })
541      // The type of the @Link decorated variable must be the same as that of the @State decorated data source.
542      LinkChild({ testNum: this.testNum.c })
543    }
544  }
545}
546```
547
548In the example, the type of **\@Link testNum: number** and the initialization from the parent component **LinkChild ({testNum:this.testNum.c})** are incorrect. The data source of \@Link must be a decorated state variable. The \@Link decorated variables must be of the same type as the data source, for example, \@Link: T and \@State: T. Therefore, the value should be changed to **\@Link testNum: ClassA**, and the initialization from the parent component should be **LinkChild({testNum: this.testNum})**.
549
550[Positive example]
551
552```ts
553@Observed
554class ClassA {
555  public c: number = 0;
556
557  constructor(c: number) {
558    this.c = c;
559  }
560}
561
562@Component
563struct LinkChild {
564  @Link testNum: ClassA;
565
566  build() {
567    Text(`LinkChild testNum ${this.testNum?.c}`)
568      .onClick(() => {
569        this.testNum.c += 1;
570      })
571  }
572}
573
574@Entry
575@Component
576struct Parent {
577  @State testNum: ClassA = new ClassA(1);
578
579  build() {
580    Column() {
581      Text(`Parent testNum ${this.testNum.c}`)
582        .onClick(() => {
583          this.testNum.c += 1;
584        })
585      // The type of the @Link decorated variable must be the same as that of the @State decorated data source.
586      LinkChild({ testNum: this.testNum })
587    }
588  }
589}
590```
591
592<!--no_check-->
593