HarmonyOS development: wrapBuilder passing parameters

AbnerMingAbnerMing
4 min read

Foreword

the code case is based on api13.

In the previous article, we have outlined the parameters passed by wrapBuilder. Whether it is defined locally or globally, passing parameters is very simple. Just call the builder function directly. The simple case is as follows:

@Builder
  function TextView(value: string) {
    Text(value)
  }


@Entry
  @Component
  struct Index {
    textBuilder: WrappedBuilder<[string]> = wrapBuilder(TextView)

    build() {
      Column() {
        this.textBuilder.builder("params")
      }.width("100%")
        .height("100%")
        .justifyContent(FlexAlign.Center)
    }
  }

the above parameters are passed in the UI view. It is very simple to pass parameters, but what if they are not in the UI view? This is the problem I have encountered recently. The team has used the dialog of any position I have customized, which is similar to the dialog in the following figure. One requirement is that message is not a simple text, but may be a picture and text, may be a rich text, or may be other presentations. In this case, it is not flexible enough to receive a text or other types. Therefore, I directly exposed the message component and passed it to the caller.

In this way, the rich and diverse situation of message is solved. After all, the component is passed by the caller. Although the effect is realized, the problem is raised again:

"My message is displayed according to logic, for example, to show this for true and to show another for false. How can the data be passed?"

"... Well, there are so many requirements, then forget it, you can define it yourself. "Of course, this is just your own voice, the actual situation, or nod and say, okay, solve it immediately.

Basic Case

case, here I simplified, mainly defined a pop-up window tool class.

import { ComponentContent } from "@kit.ArkUI"
import { DialogAttribute } from "./DialogAttribute"

export class DialogUtils {
  private constructor() {
  }

  private static mDialogUtils: DialogUtils

  public static get() {
    if (DialogUtils.mDialogUtils == undefined) {
      DialogUtils.mDialogUtils = new DialogUtils()
    }
    return DialogUtils.mDialogUtils
  }

  showDialog(context: UIContext, dialogAttribute?: DialogAttribute) {
    if (dialogAttribute == undefined) {
      dialogAttribute = new DialogAttribute()
    }
    this.show(context, dialogAttribute)
  }

  private show(context: UIContext, object: Object) {
    let dView = wrapBuilder<Object[]>(dialogView)
    let dialogContent: ComponentContent<Object> = new ComponentContent(context, dView, object)
    context.getPromptAction().openCustomDialog(dialogContent)
  }
}

@Builder
  function dialogView(dialogAttribute?: DialogAttribute) {
    Column() {
      Text(dialogAttribute?.title)
        .fontSize(16)
        .fontWeight(FontWeight.Bold)
        .margin({ top: 10 })

      if (dialogAttribute?.messageView != undefined) {
        dialogAttribute?.messageView.builder()
      }

      Row() {
        Text("cancel")
          .height(40)
          .textAlign(TextAlign.Center)
          .layoutWeight(1)
        Text("confirm")
          .height(40)
          .layoutWeight(1)
          .textAlign(TextAlign.Center)
      }.width("100%")
    }.width("80%")
      .backgroundColor(Color.White)
      .borderRadius(10)
  }

In the actual development, a lot of content is dynamically variable, but also need to expose these attributes, here I simply defined a property file, in the actual development, there will be a lot of attributes, here, I simply defined two, mainly for testing.

export class DialogAttribute {
  title?: 
  messageView?: WrappedBuilder<Object[]>
}

Direct call:

import { DialogUtils } from './DialogUtils'

@Builder
function messageView() {

  Text("简单测试")
    .margin({ top: 20, bottom: 20 })
}

@Entry
@Component
struct Index {
  build() {
    Column() {
      Button("click")
        .onClick(() => {
          DialogUtils.get()
            .showDialog(this.getUIContext(), {
              title: "dialog",
              messageView: wrapBuilder(messageView)
            })
        })
    }.width("100%")
    .height("100%")
    .justifyContent(FlexAlign.Center)
  }
}

through the above case, we simply created a custom pop-up window. After clicking the button, a pop-up window will pop up to support the message message custom component and realize any effect.

Then again, how do I receive parameters in the global @ Builder? That is, in the following position:

@Builder
function messageView() {

  Text("test")
    .margin({ top: 20, bottom: 20 })
}

passing Parameters

way one, intermediate transit

in the so-called intermediate transfer, we can define a variable in a class to receive the transferred data. This is the simplest. We assign values when passing on one side and take them out when using on the other side.

Define an assignment variable:

  private mParams?: Object

  private setData(params?: Object) {
    this.mParams = params
  }

  getData(): Object | undefined {
    return this.mParams
  }

when receiving data, assign values

  showDialog(context: UIContext, dialogAttribute?: DialogAttribute) {
    if (dialogAttribute == undefined) {
      dialogAttribute = new DialogAttribute()
    }
    this.setData(dialogAttribute.messageData)
    this.show(context, dialogAttribute)
  }

use:

@Builder
function messageView() {
  Text(DialogUtils.get().getData()?.toString())
    .margin({ top: 20, bottom: 20 })
}

one thing to know is that if multiple pop-up windows are shared and the assignment variable is single-column or static, remember to restore the original value when dialog is destroyed.

Way two, receive directly

because the wrapBuilder is passed to the ComponentContent object, when the wrapBuilder is used in the wrapBuilder, it is found that the parameters will be carried directly, and we can use it directly.

 let dView = wrapBuilder<Object[]>(dialogView)
  let dialogContent: ComponentContent<Object> = new ComponentContent(context, dView, object)
  context.getPromptAction().openCustomDialog(dialogContent)

Define Receive Parameters

in the attribute definition file, define the data we need to receive. Because the type of data is uncertain, we can directly define it as an Object.

Transfer Data

DialogUtils.get()
.showDialog(this.getUIContext(), {
Title: "I am a dialog popup",
messageView: wrapBuilder(messageView),
MessageData: "Simply pass a string"
})

directly receive the passed object and obtain the specified field parameters.

@Builder
function messageView(attr: DialogAttribute) {

  Text(attr.messageData?.toString())
    .margin({ top: 20, bottom: 20 })
}

In this paper, the main brief introduction, non-UI use, wrapBuilder transfer data problem, in addition to the above way, there are other ways to achieve, in the actual development, or specific problems specific analysis.

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