DEV Community

HarmonyOS
HarmonyOS

Posted on

How to transfer data between pages via a @Link

Read the original article:How to transfer data between pages via a @Link

Context

In HarmonyOS application development, developers often need to transfer data between pages. A common use case is when data is sent from one page and needs to be used inside a specific component of the target page. However, components cannot directly receive parameters in such cases. To solve this, the @Link decorator is used to bind the passed data to the component.

Description

When navigating from one page to another, the data passed as parameters should be made available inside a custom component (e.g., DrinkDescriptionView) within the target page (DrinkDescriptionPage). Since parameters cannot be injected directly into the component, the @Link mechanism allows seamless data binding between the parent page and its child components.

Solution / Approach

DrinksGridView: When a user clicks on an item, router.pushUrl is used to navigate to DrinkDescriptionPage, passing the selected Drink object as a parameter.

import {Drink} from '../viewmodel/Drink'
import {drinkList} from '../viewmodel/DrinkList'
import router from '@ohos.router';

@Component
export struct DrinksGridView {

  @State searchText: string = ''
  @State filteredDrinks: Drink[] = drinkList
  @State buttonSize: number = 100
  timer: number = 0
  @State loadingState: boolean = false
  @State createCount: number = 0;
  @State result: boolean = false;

  private filterDrinks() {
    if (this.searchText.trim() === '') {
      this.filteredDrinks = drinkList
    } else {
      const searchTerm = this.searchText.toLowerCase()
      this.filteredDrinks = drinkList.filter(item =>
      item.name.toLowerCase().includes(searchTerm) ||
        (item.description && item.description.toLowerCase().includes(searchTerm))
      )
    }
  }


  build() {
    Column() {
      Row() {
        TextInput({ placeholder: '🔍 Search Drink...'})
          .width('92%')
          .height(56)
          .margin({ top: 12, bottom: 8 })
          .padding(10)
          .fontSize(18)
          .backgroundColor('#FFFFFF')
          .borderRadius(16)
          .onChange((value: string) => {
            this.searchText = value
            this.filterDrinks()
          })
      }
      Grid() {
        ForEach(this.filteredDrinks, (item: Drink) => {
          GridItem() {
            Column() {
              Image(item.image)
                .width('100%')
                .aspectRatio(1)
                .objectFit(ImageFit.Cover)
                .borderRadius(12)

              Text(item.name)
                .fontSize(20)
                .fontWeight(FontWeight.Bold)
                .margin({ top: 4 })
            }
            .padding(8)
          }
          .backgroundColor(Color.White)
          .border({
            width: 2,
            color: '#e0e0e0',
            radius: 12
          })
          .shadow({
            radius: 4,
            color: '#20000000',
            offsetX: 1,
            offsetY: 1
          })
          .margin(4)
          .onClick(() => {
            router.pushUrl({
              url: 'pages/DrinkDescriptionPage',
              params: item
            })
          })
        })
      }
      .columnsTemplate('1fr 1fr')
      .width('100%')
      .height('100%')
      .layoutWeight(1)
    }
    .width('100%')
    .height('100%')
  }
}

Enter fullscreen mode Exit fullscreen mode

DrinkDescriptionPage: In the aboutToAppear lifecycle method, the parameter is retrieved using router.getParams() and stored in a @State variable. This state is then linked to the DrinkDescriptionView component using @Link.

import router from '@ohos.router';
import { Drink } from '../viewmodel/Drink'
import { DrinkDescriptionView } from '../view/DrinkDescriptionView'

@Entry
@Component
struct DrinkDescriptionPage {
  @State drink: Drink | null = null;

  aboutToAppear() {
    const params = router.getParams() as Drink;
    if (params) {
      this.drink = {
        name: params.name,
        image: params.image,
        description: params.description,
        degree: params.degree
      }
    }
  }

  build() {
    Column() {
      if (this.drink) {
        DrinkDescriptionView({ drink: $drink })
      } else {

      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
    .alignItems(HorizontalAlign.Center)
  }
}

Enter fullscreen mode Exit fullscreen mode

DrinkDescriptionView: With the @Link decorator, the component receives the Drink object from the parent page and displays its content (image, name, description, degree). A back button allows navigation back to the grid view.

import {Drink} from '../viewmodel/Drink';
import router from '@ohos.router';

@Component
export struct DrinkDescriptionView {
  @Link drink: Drink

  build() {
    Column() {
      if (this.drink) {
        Column() {
          Image(this.drink.image)
            .width(120)
            .height(120)
            .objectFit(ImageFit.Cover)
            .borderRadius(8)
            .margin({ bottom: 10 })

          Text(this.drink.name)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .margin({ bottom: 4 })

          Text(`%${this.drink.degree} alcohol`)
            .fontSize(14)
            .fontColor('#666')
        }
        .width('40%')
        .padding(10)
        .margin({top: 20})
        .backgroundColor('#FFF')
        .borderRadius(12)
        .shadow({ radius: 6, color: '#10000000' })
        Scroll() {
          Text(this.drink.description)
            .fontSize(16)
            .lineHeight(24)
            .padding(20)
        }
        .margin({ top: 20 })
        .width('90%')
        .height('40%')
        .borderRadius(12)
        .backgroundColor('#FFFFFF')
        .scrollable(ScrollDirection.Vertical)
        .scrollBar(BarState.On)

        Column() {
          Button('LET THE CHALLENGE BEGINS')
            .width('90%')
            .height(50)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .backgroundColor('#2E7D32')
            .fontColor(Color.White)
            .margin({ top: 20, bottom: 10 })
            .stateEffect(true)
            .shadow({ radius: 4, color: '#40000000' })
            .onClick(() => {
              router.replaceUrl({
                url: 'pages/ChallengePage',
                params: this.drink
              })
            })

          Button('BACK TO MENU')
            .width('90%')
            .height(50)
            .fontSize(18)
            .fontWeight(FontWeight.Bold)
            .backgroundColor('#F9A825')
            .margin({ bottom: 20 })
            .stateEffect(true)
            .shadow({ radius: 4, color: '#40000000' })
            .onClick(() => {
              router.back();
            })
        }
        .width('100%')
        .margin({ top: 10, bottom: 20 })

      } else {

      }
    }
    .width('100%')
    .height('100%')
    .backgroundColor('#F5F5F5')
    .alignItems(HorizontalAlign.Center)
  }
}

Enter fullscreen mode Exit fullscreen mode

Drink: A represantative model for Drink object

export interface Drink {
  name: string,
  description: string,
  image: Resource,
  degree: number
}

Enter fullscreen mode Exit fullscreen mode

This approach ensures smooth data transfer from page to component.

Key Takeaways

Use @Link to bind state from a parent page to a child component.

Use router.pushUrl to pass parameters when navigating between pages.

Retrieve parameters on the target page with router.getParams().

Combine @State (in the parent page) and @Link (in the child component) for effective data sharing.

Additional Resources

https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-arktsdoc-link

https://developer.huawei.com/consumer/en/doc/harmonyos-references/ts-container-grid

Written by Mehmet Algul

Top comments (0)