harmony 鸿蒙\@BuilderParam装饰器:引用\@Builder函数
\@BuilderParam装饰器:引用\@Builder函数
当开发者创建自定义组件并需要为其添加特定功能(例如:点击跳转操作)时,如果直接在组件内嵌入事件方法,会导致所有该自定义组件的实例都增加此功能。为了解决组件功能定制化的问题,ArkUI引入了@BuilderParam装饰器。@BuilderParam用于装饰指向@Builder方法的变量,开发者可以在初始化自定义组件时,使用不同的方式(例如:参数修改、尾随闭包、借用箭头函数等)对@BuilderParam装饰的自定义构建函数进行传参赋值。在自定义组件内部,通过调用@BuilderParam为组件增加特定功能。该装饰器用于声明任意UI描述的元素,类似于slot占位符。
在阅读本文档前,建议提前阅读:\@Builder。
说明:
从API version 9开始,该装饰器支持在ArkTS卡片中使用。
从API version 11开始,该装饰器支持在原子化服务中使用。
装饰器使用说明
初始化\@BuilderParam装饰的方法
\@BuilderParam装饰的方法只能被自定义构建函数(\@Builder装饰的方法)初始化。
- 使用所属自定义组件的自定义构建函数或者全局的自定义构建函数,在本地初始化\@BuilderParam。
@Builder
function overBuilder() {
}
@Component
struct Child {
@Builder
doNothingBuilder() {
}
// 使用自定义组件的自定义构建函数初始化@BuilderParam
@BuilderParam customBuilderParam: () => void = this.doNothingBuilder;
// 使用全局自定义构建函数初始化@BuilderParam
@BuilderParam customOverBuilderParam: () => void = overBuilder;
build() {
}
}
- 用父组件自定义构建函数初始化子组件\@BuilderParam装饰的方法。
@Component
struct Child {
@Builder
customBuilder() {
}
@BuilderParam customBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customBuilderParam()
}
}
}
@Entry
@Component
struct Parent {
@Builder
componentBuilder() {
Text(`Parent builder `)
}
build() {
Column() {
Child({ customBuilderParam: this.componentBuilder })
}
}
}
图1 示例效果图
- 需要注意this的指向。
this指向示例如下:
```ts
@Component
struct Child {
label: string = 'Child';
@Builder
customBuilder() {
}
@Builder
customChangeThisBuilder() {
}
@BuilderParam customBuilderParam: () => void = this.customBuilder;
@BuilderParam customChangeThisBuilderParam: () => void = this.customChangeThisBuilder;
build() {
Column() {
this.customBuilderParam()
this.customChangeThisBuilderParam()
}
}
}
@Entry
@Component
struct Parent {
label: string = 'Parent';
@Builder
componentBuilder() {
Text(`${this.label}`)
}
build() {
Column() {
// 调用this.componentBuilder()时,this指向当前@Entry所装饰的Parent组件,即label变量的值为"Parent"。
this.componentBuilder()
Child({
// 把this.componentBuilder传给子组件Child的@BuilderParam customBuilderParam,this指向的是子组件Child,即label变量的值为"Child"。
customBuilderParam: this.componentBuilder,
// 把():void=>{this.componentBuilder()}传给子组件Child的@BuilderParam customChangeThisBuilderParam,
// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent"。
customChangeThisBuilderParam: (): void => {
this.componentBuilder()
}
})
}
}
}
```
图2 示例效果图
限制条件
使用
@BuilderParam
装饰的变量只能通过@Builder
函数进行初始化。具体参见@BuilderParam装饰器初始化的值必须为@Builder。当@Require装饰器和@BuilderParam装饰器一起使用时,@BuilderParam装饰器必须进行初始化。具体请参见@Require装饰器和@BuilderParam装饰器联合使用。
在自定义组件尾随闭包的场景下,子组件有且仅有一个\@BuilderParam用来接收此尾随闭包,且此\@BuilderParam不能有参数。详情见尾随闭包初始化组件。
使用场景
参数初始化组件
@BuilderParam
装饰的方法为有参数或无参数两种形式,需与指向的@Builder
方法类型匹配。
class Tmp {
label: string = '';
}
@Builder
function overBuilder($$: Tmp) {
Text($$.label)
.width(400)
.height(50)
.backgroundColor(Color.Green)
}
@Component
struct Child {
label: string = 'Child';
@Builder
customBuilder() {
}
// 无参数类型,指向的customBuilder也是无参数类型
@BuilderParam customBuilderParam: () => void = this.customBuilder;
// 有参数类型,指向的overBuilder也是有参数类型的方法
@BuilderParam customOverBuilderParam: ($$: Tmp) => void = overBuilder;
build() {
Column() {
this.customBuilderParam()
this.customOverBuilderParam({ label: 'global Builder label' })
}
}
}
@Entry
@Component
struct Parent {
label: string = 'Parent';
@Builder
componentBuilder() {
Text(`${this.label}`)
}
build() {
Column() {
this.componentBuilder()
Child({ customBuilderParam: this.componentBuilder, customOverBuilderParam: overBuilder })
}
}
}
图3 示例效果图
尾随闭包初始化组件
在自定义组件中,使用\@BuilderParam装饰的属性可通过尾随闭包进行初始化。初始化时,组件后需紧跟一个大括号“{}”形成尾随闭包场景。
说明:
此场景下自定义组件内仅有一个使用\@BuilderParam装饰的属性。
此场景下自定义组件不支持通用属性。
开发者可以将尾随闭包内的内容看作\@Builder装饰的函数传给\@BuilderParam。
示例1:
@Component
struct CustomContainer {
@Prop header: string = '';
@Builder
closerBuilder() {
}
// 使用父组件的尾随闭包{}(@Builder装饰的方法)初始化子组件@BuilderParam
@BuilderParam closer: () => void = this.closerBuilder;
build() {
Column() {
Text(this.header)
.fontSize(30)
this.closer()
}
}
}
@Builder
function specificParam(label1: string, label2: string) {
Column() {
Text(label1)
.fontSize(30)
Text(label2)
.fontSize(30)
}
}
@Entry
@Component
struct CustomContainerUser {
@State text: string = 'header';
build() {
Column() {
// 创建CustomContainer,在创建CustomContainer时,通过其后紧跟一个大括号“{}”形成尾随闭包
// 作为传递给子组件CustomContainer @BuilderParam closer: () => void的参数
CustomContainer({ header: this.text }) {
Column() {
specificParam('testA', 'testB')
}.backgroundColor(Color.Yellow)
.onClick(() => {
this.text = 'changeHeader';
})
}
}
}
}
图4 示例效果图
使用全局@Builder
和局部@Builder
通过尾随闭包的形式对@ComponentV2
装饰的自定义组件中的@BuilderParam
进行初始化。
示例2:
@ComponentV2
struct ChildPage {
@Require @Param message: string = "";
@Builder
customBuilder() {
}
@BuilderParam customBuilderParam: () => void = this.customBuilder;
build() {
Column() {
Text(this.message)
.fontSize(30)
.fontWeight(FontWeight.Bold)
this.customBuilderParam()
}
}
}
const builder_value: string = 'Hello World';
@Builder
function overBuilder() {
Row() {
Text(`全局 Builder: ${builder_value}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@ComponentV2
struct ParentPage {
@Local label: string = 'Parent Page';
@Builder
componentBuilder() {
Row() {
Text(`局部 Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
ChildPage({ message: this.label }) {
Column() { // 使用局部@Builder,通过组件后紧跟一个大括号“{}”形成尾随闭包去初始化自定义组件@BuilderParam
this.componentBuilder();
}
}
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
ChildPage({ message: this.label }) { // 使用全局@Builder,通过组件后紧跟一个大括号“{}”形成尾随闭包去初始化自定义组件@BuilderParam
Column() {
overBuilder();
}
}
}
}
}
使用\@BuilderParam隔离多组件对\@Builder跳转逻辑的调用
当@Builder封装的系统组件包含跳转逻辑时,所有调用该@Builder的自定义组件将具备该跳转功能。对于需要禁用跳转的特定组件,可使用@BuilderParam来隔离跳转逻辑。
说明:
当前示例代码中使用了Navigation组件导航,具体实现逻辑可以查询Navigation指南。
import { HelloWorldPageBuilder } from './helloworld';
class navigationParams {
pathStack: NavPathStack = new NavPathStack();
boo: boolean = true;
}
@Builder
function navigationAction(params: navigationParams) {
Column() {
Navigation(params.pathStack) {
Button('router to page', { stateEffect: true, type: ButtonType.Capsule })
.width('80%')
.height(40)
.margin(20)
.onClick(() => {
// 通过修改@BuilderParam参数决定是否跳转。
if (params.boo) {
params.pathStack.pushPath({ name: "HelloWorldPage" });
} else {
console.info('@BuilderParam setting does not jump');
}
})
}
.navDestination(HelloWorldPageBuilder)
.hideTitleBar(true)
.height('100%')
.width('100%')
}
.height('25%')
.width('100%')
}
@Entry
@Component
struct ParentPage {
@State info: navigationParams = new navigationParams();
build() {
Column() {
Text('ParentPage')
navigationAction({ pathStack: this.info.pathStack, boo: true })
ChildPageOne()
ChildPage_BuilderParam({ eventBuilder: navigationAction })
}
.height('100%')
.width('100%')
}
}
@Component
struct ChildPageOne {
@State info: navigationParams = new navigationParams();
build() {
Column() {
Text('ChildPage')
navigationAction({ pathStack: this.info.pathStack, boo: true })
}
}
}
@Component
struct ChildPage_BuilderParam {
@State info: navigationParams = new navigationParams();
@BuilderParam eventBuilder: (param: navigationParams) => void = navigationAction;
build() {
Column() {
Text('ChildPage_BuilderParam')
// 对传递过来的全局@Builder进行参数修改,可以实现禁用点击跳转的功能。
this.eventBuilder({ pathStack: this.info.pathStack, boo: false })
}
}
}
// helloworld.ets
@Builder
export function HelloWorldPageBuilder() {
HelloWorldPage()
}
@Component
struct HelloWorldPage {
@State message: string = 'Hello World';
@State pathStack: NavPathStack = new NavPathStack();
build() {
NavDestination() {
Column() {
Text(this.message)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
.height('100%')
.width('100%')
}
}
// router_map.json
{
"routerMap": [
{
"name": "HelloWorldPage",
"buildFunction": "HelloWorldPageBuilder",
"pageSourceFile": "src/main/ets/pages/helloworld.ets"
}
]
}
// module.json5
{
"module": {
"routerMap": "$profile:router_map",
......
}
}
使用全局和局部\@Builder初始化\@BuilderParam
在自定义组件中,使用\@BuilderParam装饰的变量接收父组件通过\@Builder传递的内容进行初始化,由于父组件的\@Builder可以使用箭头函数改变当前的this指向,因此使用\@BuilderParam装饰的变量会展示不同的内容。
@Component
struct ChildPage {
label: string = 'Child Page';
@Builder
customBuilder() {
}
@BuilderParam customBuilderParam: () => void = this.customBuilder;
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customBuilderParam()
this.customChangeThisBuilderParam()
}
}
}
const builder_value: string = 'Hello World';
@Builder
function overBuilder() {
Row() {
Text(`全局 Builder: ${builder_value}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@Component
struct ParentPage {
label: string = 'Parent Page';
@Builder
componentBuilder() {
Row() {
Text(`局部 Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
// 调用this.componentBuilder()时,this指向当前@Entry所装饰的ParentPage组件,所以label变量的值为"Parent Page"。
this.componentBuilder()
ChildPage({
// 把this.componentBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向的是子组件ChildPage,所以label变量的值为"Child Page"。
customBuilderParam: this.componentBuilder,
// 把():void=>{this.componentBuilder()}传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,
// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent Page"。
customChangeThisBuilderParam: (): void => {
this.componentBuilder()
}
})
Line()
.width('100%')
.height(10)
.backgroundColor('#000000').margin(10)
// 调用全局overBuilder()时,this指向当前整个活动页,所以展示的内容为"Hello World"。
overBuilder()
ChildPage({
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customBuilderParam: overBuilder,
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customChangeThisBuilderParam: overBuilder
})
}
}
}
图5 示例效果图
在@ComponentV2装饰的自定义组件中使用@BuilderParam
使用全局@Builder和局部@Builder初始化@ComponentV2装饰的自定义组件中的@BuilderParam属性。
@ComponentV2
struct ChildPage {
@Param label: string = 'Child Page';
@Builder
customBuilder() {
}
@BuilderParam customBuilderParam: () => void = this.customBuilder;
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customBuilderParam()
this.customChangeThisBuilderParam()
}
}
}
const builder_value: string = 'Hello World';
@Builder
function overBuilder() {
Row() {
Text(`全局 Builder: ${builder_value}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
@Entry
@ComponentV2
struct ParentPage {
@Local label: string = 'Parent Page';
@Builder
componentBuilder() {
Row() {
Text(`局部 Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
// 调用this.componentBuilder()时,this指向当前@Entry所装饰的ParentPage组件,所以label变量的值为"Parent Page"。
this.componentBuilder()
ChildPage({
// 把this.componentBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向的是子组件ChildPage,所以label变量的值为"Child Page"。
customBuilderParam: this.componentBuilder,
// 把():void=>{this.componentBuilder()}传给子组件ChildPage的@BuilderParam customChangeThisBuilderPara
// 因为箭头函数的this指向的是宿主对象,所以label变量的值为"Parent Page"。
customChangeThisBuilderParam: (): void => {
this.componentBuilder()
}
})
Line()
.width('100%')
.height(5)
.backgroundColor('#000000').margin(10)
// 调用全局overBuilder()时,this指向当前整个活动页,所以展示的内容为"Hello World"。
overBuilder()
ChildPage({
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customBuilderParam: overBuilder,
// 把全局overBuilder传给子组件ChildPage的@BuilderParam customChangeThisBuilderParam,this指向当前整个活动页,所以展示的内容为"Hello World"。
customChangeThisBuilderParam: overBuilder
})
}
}
}
图6 示例效果图
常见问题
改变内容UI不刷新
调用自定义组件ChildPage时,通过this.componentBuilder
形式传递@Builder
参数。由于this
指向自定义组件内部,因此在父组件中改变label
的值时,自定义组件ChildPage无法感知到这一变化。
【反例】
@Component
struct ChildPage {
@State label: string = 'Child Page';
@Builder
customBuilder() {
}
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customChangeThisBuilderParam()
}
}
}
@Entry
@Component
struct ParentPage {
@State label: string = 'Parent Page';
@Builder
componentBuilder() {
Row() {
Text(`Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
ChildPage({
// 当前写法this指向ChildPage组件内
customChangeThisBuilderParam: this.componentBuilder
})
Button('点击改变label内容')
.onClick(() => {
this.label = 'Hello World';
})
}
}
}
使用箭头函数将@Builder
传递到自定义组件ChildPage
中,这样this
指向会停留在父组件ParentPage
里。因此,在父组件中改变label
的值时,ChildPage
会感知到并重新渲染UI。
【正例】
@Component
struct ChildPage {
@State label: string = 'Child Page';
@Builder
customBuilder() {
}
@BuilderParam customChangeThisBuilderParam: () => void = this.customBuilder;
build() {
Column() {
this.customChangeThisBuilderParam()
}
}
}
@Entry
@Component
struct ParentPage {
@State label: string = 'Parent Page';
@Builder
componentBuilder() {
Row() {
Text(`Builder :${this.label}`)
.fontSize(20)
.fontWeight(FontWeight.Bold)
}
}
build() {
Column() {
ChildPage({
customChangeThisBuilderParam: () => {
this.componentBuilder()
}
})
Button('点击改变label内容')
.onClick(() => {
this.label = 'Hello World';
})
}
}
}
@Require装饰器和@BuilderParam装饰器联合使用
由于@Require装饰器所装饰的变量需进行初始化,若变量未初始化,在编译时会输出报错信息。
【反例】
@Builder
function globalBuilder() {
Text('Hello World')
}
@Entry
@Component
struct customBuilderDemo {
build() {
Column() {
// 由于未对子组件ChildBuilder进行赋值,此处无论是编译还是编辑,均会报错。
ChildPage()
}
}
}
@Component
struct ChildPage {
@Require @BuilderParam ChildBuilder: () => void = globalBuilder;
build() {
Column() {
this.ChildBuilder()
}
}
}
对@Require装饰的变量进行外部传入初始化。
【正例】
@Builder
function globalBuilder() {
Text('Hello World')
}
@Entry
@Component
struct customBuilderDemo {
build() {
Column() {
ChildPage({ ChildBuilder: globalBuilder })
}
}
}
@Component
struct ChildPage {
@Require @BuilderParam ChildBuilder: () => void = globalBuilder;
build() {
Column() {
this.ChildBuilder()
}
}
}
@BuilderParam装饰器初始化的值必须为@Builder
使用@State
装饰器装饰的变量,给子组件的@BuilderParam
和ChildBuilder
变量初始化时,编译时会输出报错信息。
【反例】
@Builder
function globalBuilder() {
Text('Hello World')
}
@Entry
@Component
struct customBuilderDemo {
@State message: string = "";
build() {
Column() {
// 子组件ChildBuilder接收@State装饰的变量,会出现编译和编辑报错
ChildPage({ ChildBuilder: this.message })
}
}
}
@Component
struct ChildPage {
@BuilderParam ChildBuilder: () => void = globalBuilder;
build() {
Column() {
this.ChildBuilder()
}
}
}
使用全局@Builder
装饰的globalBuilder()
方法为子组件@BuilderParam
装饰的ChildBuilder
变量进行初始化,编译时无报错,功能正常。
【正例】
@Builder function globalBuilder() {
Text('Hello World')
}
@Entry
@Component
struct customBuilderDemo {
build() {
Column() {
ChildPage({ChildBuilder: globalBuilder})
}
}
}
@Component
struct ChildPage {
@BuilderParam ChildBuilder: () => void = globalBuilder;
build() {
Column() {
this.ChildBuilder()
}
}
}
你可能感兴趣的鸿蒙文章
harmony 鸿蒙\@AnimatableExtend装饰器:定义可动画属性
harmony 鸿蒙AppStorage:应用全局的UI状态存储
热门推荐
-
2、 - 优质文章
-
3、 gate.io
-
8、 golang
-
9、 openharmony
-
10、 Vue中input框自动聚焦