• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# wrapBuilder: Encapsulating Global @Builder
2
3  When multiple global @Builder functions are used within a single struct to implement different UI effects, code maintenance becomes challenging and the page structure appears cluttered. **wrapBuilder** provides a solution to encapsulate these global builders.
4
5  Before reading this topic, you are advised to read [\@Builder](./arkts-builder.md).
6
7> **NOTE**
8>
9> This feature is supported since API version 11.
10
11When the @Builder method is assigned to a variable or array, it cannot be used in the UI method.
12
13```ts
14@Builder
15function builderElement() {}
16
17let builderArr: Function[] = [builderElement];
18@Builder
19function testBuilder() {
20  ForEach(builderArr, (item: Function) => {
21    item();
22  })
23}
24```
25
26In the preceding code, **builderArr** is an array of @Builder methods. When you obtain each @Builder method in the ForEach loop, an issue arises where the @Builder method cannot be used in the UI method.
27
28To address this issue, **wrapBuilder** is introduced as a global @Builder encapsulation function. **wrapBuilder** returns a **WrappedBuilder** object, enabling [global @Builder functions](arkts-builder.md#global-custom-builder-function) to be assigned and passed.
29
30## Available APIs
31
32**wrapBuilder** is a template function that returns a **WrappedBuilder** object.
33
34```ts
35declare function wrapBuilder<Args extends Object[]>(builder: (...args: Args) => void): WrappedBuilder<Args>;
36```
37The **WrappedBuilder** object is also a template class.
38
39```ts
40declare class WrappedBuilder<Args extends Object[]> {
41  builder: (...args: Args) => void;
42
43  constructor(builder: (...args: Args) => void);
44}
45```
46
47
48>**NOTE**<br>The template parameter **Args extends Object[]** is a parameter list of the builder function to be wrapped.
49
50Example
51
52```ts
53let builderVar: WrappedBuilder<[string, number]> = wrapBuilder(MyBuilder);
54let builderArr: WrappedBuilder<[string, number]>[] = [wrapBuilder(MyBuilder)]; // Can be placed in arrays.
55```
56
57
58
59## Constraints
60
611. **wrapBuilder** only accepts a [global \@Builder decorated function](arkts-builder.md#global-custom-builder-function) as its argument.
62
632. Of the **WrappedBuilder** object it returns, the **builder** attribute method can be used only inside the struct.
64
65## Assigning a Value to a Variable Using the @Builder Method
66
67To solve the issue where an @Builder decorated method (for example, **MyBuilder**) cannot be used after being assigned to a variable, you can pass the method as a parameter to **wrapBuilder**, and then assign the return value of **wrapBuilder** to the variable **globalBuilder**.
68
69```ts
70@Builder
71function MyBuilder(value: string, size: number) {
72  Text(value)
73    .fontSize(size)
74}
75
76let globalBuilder: WrappedBuilder<[string, number]> = wrapBuilder(MyBuilder);
77
78@Entry
79@Component
80struct Index {
81  @State message: string = 'Hello World';
82
83  build() {
84    Row() {
85      Column() {
86        globalBuilder.builder(this.message, 50)
87      }
88      .width('100%')
89    }
90    .height('100%')
91  }
92}
93```
94
95##  Assigning a Value to a Variable by the @Builder Method to Use the Variable in UI Syntax
96
97In this example, the custom component **Index** uses **ForEach** to render different \@Builder functions. You can use the **wrapBuilder** array declared in **builderArr** to achieve different \@Builder function effects. This results in cleaner and more organized code.
98
99```
100@Builder
101function MyBuilder(value: string, size: number) {
102  Text(value)
103    .fontSize(size)
104}
105
106@Builder
107function YourBuilder(value: string, size: number) {
108  Text(value)
109    .fontSize(size)
110    .fontColor(Color.Pink)
111}
112
113const builderArr: WrappedBuilder<[string, number]>[] = [wrapBuilder(MyBuilder), wrapBuilder(YourBuilder)];
114
115
116@Entry
117@Component
118struct Index {
119  @Builder
120  testBuilder() {
121    ForEach(builderArr, (item: WrappedBuilder<[string, number]>) => {
122      item.builder('Hello World', 30)
123    }
124
125    )
126  }
127
128  build() {
129    Row() {
130      Column() {
131        this.testBuilder()
132      }
133      .width('100%')
134    }
135    .height('100%')
136  }
137}
138```
139
140## Passing Parameters by Reference
141
142When parameters are passed by reference, changes to state variables will trigger UI updates inside the @Builder method.
143
144```ts
145class Tmp {
146  paramA2: string = 'hello';
147}
148
149@Builder
150function overBuilder(param: Tmp) {
151  Column() {
152    Text(`wrapBuildervalue:${param.paramA2}`)
153  }
154}
155
156const wBuilder: WrappedBuilder<[Tmp]> = wrapBuilder(overBuilder);
157
158@Entry
159@Component
160struct Parent {
161  @State label: Tmp = new Tmp();
162
163  build() {
164    Column() {
165      wBuilder.builder({ paramA2: this.label.paramA2 })
166      Button('Click me').onClick(() => {
167        this.label.paramA2 = 'ArkUI';
168      })
169    }
170  }
171}
172```
173
174## FAQs
175
176### Failure of Duplicate wrapBuilder Initialization
177
178In the same custom component, the same **wrapBuilder** can be initialized only once. In the example, after **builderObj** is initialized through **wrapBuilder(MyBuilderFirst)**, re-assigning it with **wrapBuilder(MyBuilderSecond)** will not take effect.
179
180```ts
181@Builder
182function MyBuilderFirst(value: string, size: number) {
183  Text('MyBuilderFirst: ' + value)
184    .fontSize(size)
185}
186
187@Builder
188function MyBuilderSecond(value: string, size: number) {
189  Text('MyBuilderSecond: ' + value)
190    .fontSize(size)
191}
192
193interface BuilderModel {
194  globalBuilder: WrappedBuilder<[string, number]>;
195}
196
197@Entry
198@Component
199struct Index {
200  @State message: string = 'Hello World';
201  @State builderObj: BuilderModel = { globalBuilder: wrapBuilder(MyBuilderFirst) };
202
203  aboutToAppear(): void {
204    setTimeout(() => {
205      // wrapBuilder(MyBuilderSecond) does not take effect.
206      this.builderObj.globalBuilder = wrapBuilder(MyBuilderSecond);
207    }, 1000);
208  }
209
210  build() {
211    Row() {
212      Column() {
213        this.builderObj.globalBuilder.builder(this.message, 20)
214      }
215      .width('100%')
216    }
217    .height('100%')
218  }
219}
220```
221