当应用中父子组件需要数据同步时,可以使用@Prop和@Link装饰器
一个组件被引用时,引用其他组件的组件就是父组件、被引用的组件就是子组件。
由于@State装饰器无法执行父子之间的数据通信
Prop单向传递
语法: 将一个显示任务数量和完成情况的卡片组件进行封装
@Component
struct TaskStatic {
@Prop finishTask: number // Prop变量不需要初始化
@Prop totalTask: number
build() {
// ===任务数量显示卡片st===//
Row() {
Text('任务进度:') // 任务进度文字
.whiteFont()
.fontSize(30)
.fontWeight(FontWeight.Bold)
Stack() { // 叠加容器,容器内组件依次入栈,后一个组件覆盖前一个组件
// ===进度条st===//
Progress({ value: this.finishTask, total: this.totalTask, type: ProgressType.Ring })
.width(100)
// ===进度条end===//
Row() { // 两个文本,一个显示完成任务数量,一个显示总的任务数量
Text(this.finishTask.toString()) // 完成任务
.fontSize(24)
.fontColor('#76D')
Text(`/` + this.totalTask.toString()) // 所有任务
.fontSize(24)
.whiteFont()
}
}
}
.card()
.justifyContent(FlexAlign.SpaceEvenly)
// ===任务数量显示卡片end===//
}
}
入口代码:
@Entry
@Component
struct PropPage {
// 总的任务量
@State totalTask: number = 0
// 完成的任务
@State finishTask: number = 0
build() {
Column() {
// 标题栏st//
Header({ title: '任务统计案例' })
// 任务展示卡片组件 prop单向同步显示//
TaskStatic({ finishTask: this.finishTask, totalTask: this.totalTask })
// 任务列表组件//
TaskList({finishTask:$finishTask,totalTask:$totalTask})
}
.width('100%') // Column宽度
.height('100%') // Column高度
.backgroundColor('#F1F2F3')
}
}
Link变量初始化
语法:
@Component
struct TaskList {
@Link finishTask: number // Link变量也不需要初始化
@Link totalTask: number
//任务数组
@State tasks: Task[] = []
handleTaskChange() {
this.totalTask = this.tasks.length // 所有的任务
// 遍历数组,过滤出已经完成的任务数量 this.finishTask = this.tasks.filter(item => item.finished).length
}
build() { // 子元素中必须只有一个根元素
Column() {
// ==新增任务按钮st==//
Button('新增任务')
.onClick(() => {
// 1. 新增任务进入数组
this.tasks.push(new Task())
// 2. 改变任务总数
this.handleTaskChange()
})
.width(100)
.height(40)
.type(ButtonType.Normal)
.borderRadius(15)
.margin(10)
// 新增任按钮end//
// 渲染列表st//
List({ space: 10 }) {
ForEach(this.tasks, (item: Task, index) => {
ListItem() {
Row() {
Text(`新增任务${index}`)
.fontSize(24)
.whiteFont()
Checkbox({ name: 'checkbox' + index.toString() })
.select(item.finished)
.selectedColor(0xed6f21)
.onChange((value: boolean) => {
item.finished = value
this.handleTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
.margin(6)
}
.swipeAction({ end: this.deleteButton(index) }) // 列表的右滑功能函数
})
} .width('100%')
.layoutWeight(1) // 将剩下的空间全部给list元素
.alignListItem(ListItemAlign.Center) // 列表元素居中
// =渲染列表end=//
}
}
@Builder deleteButton(index: number) { // 删除按钮构建函数
Button() {
Image($r('app.media.delete'))
.width(40)
.padding(5)
} .width(60)
.type(ButtonType.Circle)
.backgroundColor(Color.Brown)
.onClick(() => {
this.tasks.splice(index, 1) // 删除1个
this.handleTaskChange() // 一旦更改任务,就需要重新渲染
})
}
}
TaskList({finishTask:$finishTask,totalTask:$totalTask})}
传值的时候需要传变量的引用:$+变量名
总结
父组件影响子组件用Prop,父子组件相互影响需要用Link,互不影响使用State
其次,允许装饰的变量类型也不一样,Prop只支持string、number、boolean、enum类型,父组件对象类型,子组件是对象属性。不可以是数组、any类型。
而Link,父子类型一致,string、number、boolean、enum、object、class,以及他们的数组。数组中元素增删、替换都会引起刷新。嵌套类型以及数组中的对象属性无法触发视图更新。
初始化方式上看,Prop不允许子组件初始化,Link由父组件传递,禁止子组件初始化。
也提供了另一种传递方式,@Provide 和@Consume,不需要初始化和传值。
import router from '@ohos.router'
import { Header } from '../components/herder'
// 创建Task类
class Task {
static id: number = 1
name: string = `任务${Task.id++}`
finished: boolean = false
}
// 创建单独的卡片样式
@Styles function card() {
.width('95%')
// .height(20)
.padding(20)
.backgroundColor(Color.Brown)
.borderRadius(15)
.shadow({ radius: 6, color: '#1F000000', offsetX: 2, offsetY: 4 })
}
// 任务完成样式
@Extend(Text) function finishedCard() {
.decoration({ type: TextDecorationType.LineThrough })
.fontColor('#B1B2B1')
}
// 任务白色字体
@Extend(Text) function whiteFont() {
.fontColor(Color.White)
}
@Entry
@Component
struct PropPage {
// 总的任务量
@Provide totalTask: number = 0
// 完成的任务
@Provide finishTask: number = 0
build() {
Column() {
// =======================标题栏st======================//
Header({ title: '任务统计案例' })
// =======================任务展示卡片组件======================//
TaskStatic()
// =======================任务列表组件======================//
TaskList()
} .width('100%') // Column宽度
.height('100%') // Column高度
.backgroundColor('#F1F2F3')
}
}
@Component
struct TaskStatic {
@Consume finishTask: number
@Consume totalTask: number
build() {
// =======================任务数量显示卡片st======================//
Row() {
Text('任务进度:') // 任务进度文字
.whiteFont()
.fontSize(30)
.fontWeight(FontWeight.Bold)
Stack() { // 叠加容器,容器内组件依次入栈,后一个组件覆盖前一个组件
// =======================进度条st======================// Progress({ value: this.finishTask, total: this.totalTask, type: ProgressType.Ring })
.width(100)
// =======================进度条end======================//
Row() { // 两个文本,一个显示完成任务数量,一个显示总的任务数量
Text(this.finishTask.toString()) // 完成任务
.fontSize(24)
.fontColor('#76D')
Text(`/` + this.totalTask.toString()) // 所有任务
.fontSize(24)
.whiteFont()
} }
}
.card()
.justifyContent(FlexAlign.SpaceEvenly)
// =======================任务数量显示卡片end======================//
}
}
@Component
struct TaskList {
@Consume finishTask: number
@Consume totalTask: number
//任务数组
@State tasks: Task[] = []
handleTaskChange() {
this.totalTask = this.tasks.length // 所有的任务
// 遍历数组,过滤出已经完成的任务数量 this.finishTask = this.tasks.filter(item => item.finished).length
}
build() { // 子元素中必须只有一个根元素
Column() {
// =======================新增任务按钮st======================//
Button('新增任务')
.onClick(() => {
// 1. 新增任务进入数组
this.tasks.push(new Task())
// 2. 改变任务总数
this.handleTaskChange()
})
.width(100)
.height(40)
.type(ButtonType.Normal)
.borderRadius(15)
.margin(10)
// =======================新增任按钮end======================//
// =======================渲染列表st======================// List({ space: 10 }) {
ForEach(this.tasks, (item: Task, index) => {
ListItem() {
Row() {
Text(`新增任务${index}`)
.fontSize(24)
.whiteFont()
Checkbox({ name: 'checkbox' + index.toString() })
.select(item.finished)
.selectedColor(0xed6f21)
.onChange((value: boolean) => {
item.finished = value
this.handleTaskChange()
})
}
.card()
.justifyContent(FlexAlign.SpaceBetween)
.margin(6)
}
.swipeAction({ end: this.deleteButton(index) }) // 列表的右滑功能函数
})
} .width('100%')
.layoutWeight(1) // 将剩下的空间全部给list元素
.alignListItem(ListItemAlign.Center) // 列表元素居中
// =======================渲染列表end======================// }
}
@Builder deleteButton(index: number) { // 删除按钮构建函数
Button() {
Image($r('app.media.delete'))
.width(40)
.padding(5)
} .width(60)
.type(ButtonType.Circle)
.backgroundColor(Color.Brown)
.onClick(() => {
this.tasks.splice(index, 1) // 删除1个
this.handleTaskChange() // 一旦更改任务,就需要重新渲染
})
}
}