DEV Community

Cover image for HarmonyOS development: wrapBuilder to encapsulate global @ Builder
程序员一鸣
程序员一鸣

Posted on

HarmonyOS development: wrapBuilder to encapsulate global @ Builder

Foreword

this article code case based on api13.

@ Builder decorator can extract the component code in the build function separately. Although the build function is simplified and the reuse between components is realized, the code is still in the entire UI view, as shown in the following case:

@Entry
@Component
struct Index {
  @Builder
  TextView(text: string) {
    Text(text)
  }

  build() {
    Column() {
      this.TextView("test data 1")
      this.TextView("test data 2")
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

it can only be said that in the same UI view, component reuse is realized and the code is simplified. However, what if there are many pages sharing a component? Fortunately, the @ Builder decorator supports global definitions, which have been outlined in previous articles, and can extract common components into a global file.

@Builder
export function TextView(text: string) {
  Text(text)
}
Enter fullscreen mode Exit fullscreen mode

Call directly where needed.

import { TextView } from './Views'

@Entry
@Component
struct Index {

  build() {
    Column() {
     TextView("test data 1")
     TextView("test data 2")
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

Well, looking back, we found that the @ Builder decorator can be local or global, which has already met the demand. What do you need wrapBuilder? Don't worry, let's look at these scenes first:

  1. There is a custom Dialog pop-up window that needs to receive the UI view transferred from the outside. How do I transfer the component?

  2. There is an encapsulated Network Library, one of which is to request Loading. Since the Loading of each project is different, this Loading component view needs to be passed separately. How?

How can I pass externally defined UI components to where they are needed? These needed places are not modified by struct. Let's think about it. Can they be passed on directly?

Let's look at another case. Although we can define a global @ Builder decorator, when I combine it into an array, I cannot call it, as follows:

import { TextView } from './Views';


let builderArr: Function[] = [TextView]


@Entry
@Component
struct Index {

  build() {
    Column() {

      ForEach(builderArr, (item: Function) => {
        item()
      })

    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

direct error reporting:

'item()' does not comply with the UI component syntax. <ArkTSCheck>
Enter fullscreen mode Exit fullscreen mode

in order to solve the above problems,Hongmeng introduces wrapBuilder as a global @ Builder encapsulation function and returns a WrappedBuilder object to implement global @ Builder assignments and passes can be made.

How to use

the wrapBuilder is a template function that returns a WrappedBuilder object.

declare function wrapBuilder< Args extends Object[]>(builder: (...args: Args) => void): WrappedBuilder;
Enter fullscreen mode Exit fullscreen mode

Declaring Variables

let builderVar: WrappedBuilder<[string, number]> = wrapBuilder(MyBuilder)
Enter fullscreen mode Exit fullscreen mode

simple case:

import { TextView } from './Views'

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

  build() {
    Column() {
      this.textView.builder("test data")
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

of course, variables can also be declared directly where the component is defined.

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

export let textView: WrappedBuilder<[string]> = wrapBuilder(TextView)
Enter fullscreen mode Exit fullscreen mode

Call:

import { textView } from './Views'

@Entry
@Component
struct Index {
  build() {
    Column() {
      textView.builder("test data")
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

transfer Data

in fact, in the above case, it is already a case of passing parameters, following the principle of passing whatever type is received.

For example, it receives a string type.

let textView: WrappedBuilder<[string]> = wrapBuilder(TextView)
Enter fullscreen mode Exit fullscreen mode

For example, receive two parameters, one is string and the other is number.

@Builder
function TextView(test: string,num:number) {
  Text(test)
}

export let textView: WrappedBuilder<[string,number]> = wrapBuilder(TextView)
Enter fullscreen mode Exit fullscreen mode

If too many parameters are passed, they can be passed in the form of objects, arrays, collections, etc. Here is a reference type:

defining Objects

export class TestBean {
  testData?: string = "test data"
}
Enter fullscreen mode Exit fullscreen mode

creating a Global Component

import { TestBean } from "./TestBean"

@Builder
function TextView(test: TestBean) {
  Text(test.testData)
}

export let textView: WrappedBuilder<[TestBean]> = wrapBuilder(TextView)
Enter fullscreen mode Exit fullscreen mode

component call

import { TestBean } from './TestBean';
import { textView } from './Views'

@Entry
@Component
struct Index {
  @State test: TestBean = new TestBean();

  build() {
    Column() {
      textView.builder({ testData: this.test.testData })
      Button("click").onClick(() => {
        this.test.testData = "change data"
      })
    }
    .height('100%')
    .width('100%')
    .justifyContent(FlexAlign.Center)
  }
}
Enter fullscreen mode Exit fullscreen mode

related Summary

first of all, the first point, in the same UI component, the same wrapBuilder can only be initialized once. The second point is that the builder attribute method of the WrappedBuilder object can only be used inside the struct.

How to receive a UI component from outside is very simple. You only need to receive a WrappedBuilder<[Object]>. The simple case is as follows:

export class ViewUtils {
  view?: WrappedBuilder<[Object]>

  setView(view: WrappedBuilder<[Object]>) {
    this.view = view
  }

  getView(): WrappedBuilder<[Object]> | undefined {
    return this.view
  }
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)