HarmonyOS Development: How a Parent Component Calls a Method in a Child Component

AbnerMingAbnerMing
3 min read

Foreword

this article is based on Api13

in many scenarios, the parent component needs to trigger a method in the child component to implement some specific logic, but ArkUI is a declarative UI and cannot directly call the method in the child component, so how to implement this function?

To give a very common case, you can update the sub-component UI by calling a method in the sub-component. The simple code is as follows. Only by calling the changeUI method can the UI update be triggered.

@Component
struct Child {
  @State message: string = "I am child"

  /**
   *AUTHOR:AbnerMing
   *INTRODUCE:
   */
  changeUI() {
    this.message = "child UI updata"
  }

  build() {
    Text(this.message)
      .width("100%")
      .height(50)
      .backgroundColor(Color.Pink)
      .textAlign(TextAlign.Center)
  }
}

Method 1: @ Watch decorator

if you are using the V1 version decorator, we can easily implement it using the @ Watch decorator.

Sub-component definition @ Watch decorator

@Component
struct Child {
  @State message: string = "I am child"
  @Prop @Watch("changeUI") isChangeStatus: boolean = false

  /**
   *AUTHOR:AbnerMing
   *INTRODUCE:
   */
  changeUI() {
    this.message = "child UI updata"
  }

  build() {
    Text(this.message)
      .width("100%")
      .height(50)
      .backgroundColor(Color.Pink)
      .textAlign(TextAlign.Center)
  }
}

parent component calls

the isChangeStatus attribute defined by the child component can be used to continuously change its value.

@Entry
  @Component
  struct DemoPage {
    @State isChangeStatus: boolean = false

    build() {
      Column() {
        Child({ isChangeStatus: this.isChangeStatus })
        Button("click")
          .onClick(() => {
            this.isChangeStatus = !this.isChangeStatus
          })
      }
      .height('100%')
        .width('100%')
        .justifyContent(FlexAlign.Center)
    }
  }

Method 2: @ Monitor decorator

it is the same as the implementation of Mode 1, except that the @ Monitor decorator is V2 version, that is, if you are using V2 version decorator, you can use this.

Subcomponent definition @ Monitor decorator

@ComponentV2
struct Child {
  @Local message: string = "I am child"
  @Param isChangeStatus: boolean = false

  /**
   *AUTHOR:AbnerMing
   *INTRODUCE:child method
   */
  @Monitor("isChangeStatus")
  changeUI() {
    this.message = "child UI updata"
  }

  build() {
    Text(this.message)
      .width("100%")
      .height(50)
      .backgroundColor(Color.Pink)
      .textAlign(TextAlign.Center)
  }
}

parent component calls

@Entry
@ComponentV2
struct DemoPage {
  @Local isChangeStatus: boolean = false

  build() {
    Column() {
      Child({ isChangeStatus: this.isChangeStatus })
      Button("click")
        .onClick(() => {
          this.isChangeStatus = !this.isChangeStatus
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

method 3: interface callback

defining Callback Functions

class ChangeController {
  change = () => {}
}

child Component Implementation Callback

@Component
struct Child {
  @State message: string = "I am child"
  changeController: ChangeController = new ChangeController()

  aboutToAppear(): void {
    if (this.changeController.change != undefined) {
      this.changeController.change = () => {
        this.changeUI()
      }
    }
  }

  /**
   *AUTHOR:AbnerMing
   *INTRODUCE:child method
   */
  changeUI() {
    this.message = "child UI updata"
  }

  build() {
    Text(this.message)
      .width("100%")
      .height(50)
      .backgroundColor(Color.Pink)
      .textAlign(TextAlign.Center)
  }
}

parent component calls function

@Entry
@Component
struct DemoPage {
  changeController: ChangeController = new ChangeController()

  build() {
    Column() {
      Child({ changeController: this.changeController })
      Button("click")
        .onClick(() => {
          this.changeController.change()
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

method 4: Event subscription EventHub

Subscribe to events

@Component
struct Child {
  @State message: string = "I am child"

  aboutToAppear(): void {
    getContext().eventHub.on("changeUI", ()=>{
      this.changeUI()
    })
  }

  /**
   *AUTHOR:AbnerMing
   *INTRODUCE:child
   */
  changeUI() {
    this.message = "child UI updata"
  }

  build() {
    Text(this.message)
      .width("100%")
      .height(50)
      .backgroundColor(Color.Pink)
      .textAlign(TextAlign.Center)
  }
}

trigger Event

@Entry
@Component
struct DemoPage {
  build() {
    Column() {
      Child()
      Button("click")
        .onClick(() => {
          getContext().eventHub.emit('changeUI')
        })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}

perhaps you may have doubts. It is not enough to directly trigger the sub-component update UI by the decorator. I hope you can understand that the above is only a simple case. In actual development, there may be a lot of logic in the sub-component method, such as network requests, such as data storage, etc., which is not a simple UI update.

Of course, the implementation method cited is not complete, and there may be other ways to implement it. You can choose the appropriate one in the actual development.

0
Subscribe to my newsletter

Read articles from AbnerMing directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

AbnerMing
AbnerMing