状态共享-父子单向
@Prop
装饰的变量可以和父组件建立单向的同步关系。@Prop
装饰的变量是可变的,但是变化不会同步回其父组件。-Prop是用在子组件中的Prop 支持类型和State修饰符基本一致,并且**Prop可以给初始值,也可以不给**
完成父-子的单向同步
@Entry
@Component
struct PropCase {
@State pnum: number = 0
build() {
Row() {
Column() {
Text(this.pnum.toString())
Button("+1")
.onClick(() => {
this.pnum++
})
Divider()
Child({ num: this.pnum })
}
.width('100%')
}
.height('100%')
}
}
@Component
struct Child {
@Prop num: number
build() {
Column() {
Text("子组件")
Text(this.num.toString())
}.height(60)
.width('100%')
.backgroundColor(Color.Pink)
}
}
- 如果子组件修改这个Prop呢?我们来试试
Button("修改子组件Prop")
.onClick(() => {
this.num++
})
- 我们发现使用Prop修饰的状态,只会在当前子组件生效,不会传导到父组件,所以它属于一种单向传递
- 子组件可修改
Prop
数据值,但不同步到父组件,父组件更新后覆盖子组件Prop
数据 - 子组件可以初始化默认值
- Prop如果传递是对象类型,它只会在子组件内部生效,不会延伸到父组件-
网络相册案例
- 点个按钮,出现选择照片的相册
- 选择完成之后, 点击完成图片回显示到页面
- 基础相册封装和使用
import { GoodItem } from '../04/models'
@Entry
@Component
struct PropBigCase {
@State
showAlbum: boolean = false
@State
list: GoodItem[] = [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "assets/1.webp",
"goods_price": 108,
"goods_count": 1,
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "assets/2.webp",
"goods_price": 129,
"goods_count": 1,
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "assets/3.webp",
"goods_price": 198,
"goods_count": 1,
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "assets/4.webp",
"goods_price": 99,
"goods_count": 1,
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "assets/5.webp",
"goods_price": 156,
"goods_count": 1,
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "assets/6.webp",
"goods_price": 142.8,
"goods_count": 1,
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "assets/7.webp",
"goods_price": 219,
"goods_count": 2,
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "assets/8.webp",
"goods_price": 178,
"goods_count": 1,
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "assets/9.webp",
"goods_price": 128,
"goods_count": 1,
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "assets/10.webp",
"goods_price": 153,
"goods_count": 1,
}
]
build() {
Column() {
Button("选择图片")
.onClick(() => {
this.showAlbum = true
})
if(this.showAlbum) {
PhotoAlbum({
list: this.list,
maxSelectNumber: 2,
close: () => {
this.showAlbum = false
}
})
}
}
.width('100%')
.height('100%')
.padding(2)
}
}
@Component
struct PhotoAlbum {
@Prop
maxSelectNumber: number = 9 // 设置可选择的图片的张数
@Prop
list: GoodItem[] = []
close: () => void = () => {}
build() {
Column() {
Grid() {
// 数据的
ForEach(this.list, (item: GoodItem) => {
GridItem() {
Image(item.goods_img)
.aspectRatio(1)
.onClick(() => {
// 通过一个标记 能够知道当前的图片到底是选中还是没选中 如果选中 取消选中
// 如果没选中 则选中 可以设置最多选择9张图片
})
}
})
}
.columnsGap(2)
.rowsGap(2)
.columnsTemplate("1fr 1fr 1fr")
.layoutWeight(1)
Row() {
Button("取消")
.onClick(() => {
this.close()
})
.backgroundColor(Color.Gray)
Text(`可选${this.maxSelectNumber}张`)
Button("完成")
}
.justifyContent(FlexAlign.SpaceBetween)
.padding({
left: 10,
right: 10
})
.height(50)
.width('100%')
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height('100%')
.backgroundColor(Color.White)
.position({
x: 0,
y: 0
})
}
}
- 相册选择
import { GoodItem } from '../04/models'
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct PropBigCase {
@State
showAlbum: boolean = false
@State
list: GoodItem[] = [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "assets/1.webp",
"goods_price": 108,
"goods_count": 1,
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "assets/2.webp",
"goods_price": 129,
"goods_count": 1,
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "assets/3.webp",
"goods_price": 198,
"goods_count": 1,
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "assets/4.webp",
"goods_price": 99,
"goods_count": 1,
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "assets/5.webp",
"goods_price": 156,
"goods_count": 1,
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "assets/6.webp",
"goods_price": 142.8,
"goods_count": 1,
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "assets/7.webp",
"goods_price": 219,
"goods_count": 2,
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "assets/8.webp",
"goods_price": 178,
"goods_count": 1,
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "assets/9.webp",
"goods_price": 128,
"goods_count": 1,
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "assets/10.webp",
"goods_price": 153,
"goods_count": 1,
}
]
build() {
Column() {
Button("选择图片")
.onClick(() => {
this.showAlbum = true
})
if(this.showAlbum) {
PhotoAlbum({
list: this.list,
maxSelectNumber: 9,
close: () => {
this.showAlbum = false
}
})
}
}
.width('100%')
.height('100%')
.padding(2)
}
}
@Component
struct PhotoAlbum {
@Prop
maxSelectNumber: number = 9 // 设置可选择的图片的张数
@Prop
list: GoodItem[] = []
@State
selectPhotos: SelectPhoto[] = []
close: () => void = () => {}
// 用来选中或者取消选中图片
selectImage (item: GoodItem) {
// 通过一个标记 能够知道当前的图片到底是选中还是没选中 如果选中 取消选中
// 如果没选中 则选中 可以设置最多选择9张图片
const index = this.selectPhotos.findIndex(obj => obj.imgId === item.id)
if(index > -1) {
// 表示已经选择了 选中的化需要移除
// 数组移除
// 先找索引
// 再通过吧splice进行移除
this.selectPhotos.splice(index, 1) // 移除一个内容
promptAction.showToast({ message: '执行移除' })
}
else {
// 当选择张数小于最大张数时才可以继续
if(this.selectPhotos.length < this.maxSelectNumber) {
// 没有选中
this.selectPhotos.push({ imgUrl: item.goods_img, imgId: item.id })
}
}
}
// 获取是否显示对号
getShowSelect (item: GoodItem) {
return this.selectPhotos.findIndex(obj => obj.imgId === item.id) > -1
}
build() {
Column() {
Grid() {
// 数据的
ForEach(this.list, (item: GoodItem) => {
GridItem() {
Stack({ alignContent: Alignment.BottomEnd }) {
Image(item.goods_img)
.aspectRatio(1)
if(this.getShowSelect(item)) {
// 需要展示对号
Image($r("app.media.select"))
.width(60)
.height(60)
.fillColor(Color.Orange)
}
}
.onClick(() => {
// 通过一个标记 能够知道当前的图片到底是选中还是没选中 如果选中 取消选中
// 如果没选中 则选中 可以设置最多选择9张图片
this.selectImage(item)
})
}
})
}
.columnsGap(2)
.rowsGap(2)
.columnsTemplate("1fr 1fr 1fr")
.layoutWeight(1)
Row() {
Button("取消")
.onClick(() => {
this.close()
})
.backgroundColor(Color.Gray)
Text(`已选${this.selectPhotos.length}/ 可选${this.maxSelectNumber}张`)
Button("完成")
}
.justifyContent(FlexAlign.SpaceBetween)
.padding({
left: 10,
right: 10
})
.height(50)
.width('100%')
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height('100%')
.backgroundColor(Color.White)
.position({
x: 0,
y: 0
})
}
}
interface SelectPhoto {
imgUrl: string | ResourceColor
imgId: number
}
新的诉求
- 希望点击图片-完成图片的预览 需要使用弹出层- 两种的使用方式- dialog - sheet
- 弹窗UI的第一种方式CustomDialog
- struct这个结构体只能被 Component和CustomDialog修饰
- 必须被CustomDialog修饰
- 组件中必须有一个属性 它的类型是 CustomDialogController,名字其实无所谓
- Component的修饰符可以没有,有的话意味着它可以作为组件使用
@CustomDialog
struct PreviewDialog {
controller: CustomDialogController // 控制器
url: ResourceStr = ""
build() {
Column() {
Image(this.url)
.width('100%')
}.backgroundColor(Color.Black)
.justifyContent(FlexAlign.Center)
.height('100%')
.width("100%")
.onClick(() => {
this.controller.close()
})
}
}
- 如果需要使用弹层,需要在使用的组件或者页面中显示的声明一个对象
// 显示声明对象 类型未CustomDialogController
preview: CustomDialogController = new CustomDialogController({
builder: PreviewDialog({ url: this.selectImg }), // 这里需要传入 自定义弹层的对象
customStyle: true // 按照弹层的样式来渲染
})
:::success
customStyle的意思是否允许自定义的样式设置,因为默认的弹层是有一些定制的样式的。
:::
- 弹层就两个方法
:::success
open- 创建弹层组件-显示-会有动画的弹出
close- 销毁组件-推出-会有动画的退出
:::
- 涉及到Dialog的传值
:::success
因为open/close会创建和销毁组件,不存在缓存现象,里面的参数实际上没有任何必要用修饰符
:::
- 在父组件中调用打开
GridItem() {
Image(item.imgUrl)
.aspectRatio(1)
.borderRadius(4)
.onClick(() => {
this.selectImg = item.imgUrl
// 弹出层 显示图片
this.preview.open()
})
}
- 在本身组件中同样是可以打开和关闭
.onClick(() => {
this.controller.close()
})
- sheet的用法
:::success
- 弹出框-类似于ios的交互-底部推到屏幕中的一个区域-可能有半屏或者全屏的区域
- 不需要使用组件
- 使用的是通用属性 bindSheet,可以通过该属性设置弹出框的 变量控制,内容控制,属性控制
:::
.bindSheet($$this.showAlbum, this.getSheetBuilder(), {
showClose: false
})
:::success
$$ 为什么在这里使用?
因为需要数据驱动视图,视图发生变化,它同样需要更新数据
:::
- 视图内容
// 渲染相册的内容
// Entry修饰的方法 可以没有根节点 但是 如果有的话 必须该组件是个容器组件
@Builder
getSheetBuilder() {
// 坑点 bindSheet的内容最外层需要用原生组件才可以
Column() {
PhotoAlbum({
list: this.list,
maxSelectNumber: 5,
close: () => {
this.showAlbum = false
},
finish: (selectPhotos: SelectPhoto[]) => {
this.selectList.push(...selectPhotos)
this.showAlbum = false
}
})
}
}
:::success
诉求: 需要弹出的照片需要进行滑动- Swiper 轮播图
就是一个组件Swiper, 自动的可以实现滑动, 可以实现上下和左右, 可以双向绑定组件的当前的激活索引 index
:::
@CustomDialog
struct PreviewDialog {
controller: CustomDialogController // 控制器
urls: ResourceStr [] = []
@State
selectIndex: number = 0
build() {
Column() {
Swiper() {
ForEach(this.urls, (url: string) => {
Image(url)
.width('100%')
})
}.index($$this.selectIndex)
}
.backgroundColor(Color.Black)
.justifyContent(FlexAlign.Center)
.height('100%')
.width("100%")
.onClick(() => {
this.controller.close()
})
}
}
- 相册综合案例的完整代码
import { GoodItem } from '../04/models'
import { promptAction } from '@kit.ArkUI'
@Entry
@Component
struct PropBigCase {
@State
showAlbum: boolean = false
@State
list: GoodItem[] = [
{
"id": 1,
"goods_name": "班俏BANQIAO超火ins潮卫衣女士2020秋季新款韩版宽松慵懒风薄款外套带帽上衣",
"goods_img": "assets/1.webp",
"goods_price": 108,
"goods_count": 1,
},
{
"id": 2,
"goods_name": "嘉叶希连帽卫衣女春秋薄款2020新款宽松bf韩版字母印花中长款外套ins潮",
"goods_img": "assets/2.webp",
"goods_price": 129,
"goods_count": 1,
},
{
"id": 3,
"goods_name": "思蜜怡2020休闲运动套装女春秋季新款时尚大码宽松长袖卫衣两件套",
"goods_img": "assets/3.webp",
"goods_price": 198,
"goods_count": 1,
},
{
"id": 4,
"goods_name": "思蜜怡卫衣女加绒加厚2020秋冬装新款韩版宽松上衣连帽中长款外套",
"goods_img": "assets/4.webp",
"goods_price": 99,
"goods_count": 1,
},
{
"id": 5,
"goods_name": "幂凝早秋季卫衣女春秋装韩版宽松中长款假两件上衣薄款ins盐系外套潮",
"goods_img": "assets/5.webp",
"goods_price": 156,
"goods_count": 1,
},
{
"id": 6,
"goods_name": "ME&CITY女装冬季新款针织抽绳休闲连帽卫衣女",
"goods_img": "assets/6.webp",
"goods_price": 142.8,
"goods_count": 1,
},
{
"id": 7,
"goods_name": "幂凝假两件女士卫衣秋冬女装2020年新款韩版宽松春秋季薄款ins潮外套",
"goods_img": "assets/7.webp",
"goods_price": 219,
"goods_count": 2,
},
{
"id": 8,
"goods_name": "依魅人2020休闲运动衣套装女秋季新款秋季韩版宽松卫衣 时尚两件套",
"goods_img": "assets/8.webp",
"goods_price": 178,
"goods_count": 1,
},
{
"id": 9,
"goods_name": "芷臻(zhizhen)加厚卫衣2020春秋季女长袖韩版宽松短款加绒春秋装连帽开衫外套冬",
"goods_img": "assets/9.webp",
"goods_price": 128,
"goods_count": 1,
},
{
"id": 10,
"goods_name": "Semir森马卫衣女冬装2019新款可爱甜美大撞色小清新连帽薄绒女士套头衫",
"goods_img": "assets/10.webp",
"goods_price": 153,
"goods_count": 1,
}
]
@State
selectList: SelectPhoto[] = []
selectImg: ResourceStr = ''
selectIndex: number = 0
// 显示声明对象 类型未CustomDialogController
preview: CustomDialogController = new CustomDialogController({
builder: PreviewDialog({ urls: this.selectList.map(item => item.imgUrl), selectIndex: this.selectIndex }), // 这里需要传入 自定义弹层的对象
customStyle: true, // 按照弹层的样式来渲染
cancel: () => {
}
})
// 渲染相册的内容
// Entry修饰的方法 可以没有根节点 但是 如果有的话 必须该组件是个容器组件
@Builder
getSheetBuilder() {
// 坑点 bindSheet的内容最外层需要用原生组件才可以
Column() {
PhotoAlbum({
list: this.list,
maxSelectNumber: 5,
close: () => {
this.showAlbum = false
},
finish: (selectPhotos: SelectPhoto[]) => {
this.selectList.push(...selectPhotos)
this.showAlbum = false
}
})
}
}
build() {
Column({ space: 10 }) {
Button("选择图片")
.onClick(() => {
this.showAlbum = true
})
// 渲染选择的图片
Grid() {
ForEach(this.selectList, (item: SelectPhoto, index: number) => {
GridItem() {
Image(item.imgUrl)
.aspectRatio(1)
.borderRadius(4)
.onClick(() => {
this.selectIndex = index // 记录点击的索引
// 弹出层 显示图片
this.preview.open()
})
}
})
}
.columnsTemplate("1fr 1fr")
.columnsGap(2)
.rowsGap(2)
}
.width('100%')
.height('100%')
.padding(2)
.bindSheet($$this.showAlbum, this.getSheetBuilder(), {
showClose: false,
height: '70%'
})
// 数据变化 可以驱动视图的更新
// 视图的更新无法驱动数据的变化
}
}
@Component
struct PhotoAlbum {
@Prop
maxSelectNumber: number = 9 // 设置可选择的图片的张数
@Prop
list: GoodItem[] = []
@State
selectPhotos: SelectPhoto[] = []
close: () => void = () => {
}
finish: (selectPHOTOES: SelectPhoto[]) => void = () => {
}
// 用来选中或者取消选中图片
selectImage(item: GoodItem) {
// 通过一个标记 能够知道当前的图片到底是选中还是没选中 如果选中 取消选中
// 如果没选中 则选中 可以设置最多选择9张图片
const index = this.selectPhotos.findIndex(obj => obj.imgId === item.id)
if (index > -1) {
// 表示已经选择了 选中的化需要移除
// 数组移除
// 先找索引
// 再通过吧splice进行移除
this.selectPhotos.splice(index, 1) // 移除一个内容
promptAction.showToast({ message: '执行移除' })
}
else {
// 当选择张数小于最大张数时才可以继续
if (this.selectPhotos.length < this.maxSelectNumber) {
// 没有选中
this.selectPhotos.push({ imgUrl: item.goods_img, imgId: item.id })
}
}
}
// 获取是否显示对号
getShowSelect(item: GoodItem) {
return this.selectPhotos.findIndex(obj => obj.imgId === item.id) > -1
}
build() {
Column() {
Row() {
Button("取消")
.onClick(() => {
this.close()
})
.backgroundColor(Color.Gray)
Text(`已选${this.selectPhotos.length}/可选${this.maxSelectNumber}张`)
Button("完成")
.onClick(() => {
this.finish(this.selectPhotos)
})
}
.justifyContent(FlexAlign.SpaceBetween)
.padding({
left: 10,
right: 10
})
.zIndex(40)
.height(50)
.width('100%')
Grid() {
// 数据的
ForEach(this.list, (item: GoodItem) => {
GridItem() {
Stack({ alignContent: Alignment.BottomEnd }) {
Image(item.goods_img)
.aspectRatio(1)
if (this.getShowSelect(item)) {
// 需要展示对号
Image($r("app.media.select"))
.width(60)
.height(60)
.fillColor(Color.Orange)
}
}
.onClick(() => {
// 通过一个标记 能够知道当前的图片到底是选中还是没选中 如果选中 取消选中
// 如果没选中 则选中 可以设置最多选择9张图片
this.selectImage(item)
})
}
})
}
.columnsGap(2)
.rowsGap(2)
.columnsTemplate("1fr 1fr 1fr")
.layoutWeight(1)
}
.justifyContent(FlexAlign.SpaceBetween)
.width('100%')
.height('100%')
.backgroundColor(Color.White)
}
}
interface SelectPhoto {
imgUrl: string | ResourceStr
imgId: number
}
//
@CustomDialog
struct PreviewDialog {
controller: CustomDialogController // 控制器
urls: ResourceStr [] = []
@State
selectIndex: number = 0
build() {
Column() {
Swiper() {
ForEach(this.urls, (url: string) => {
Image(url)
.width('100%')
})
}.index($$this.selectIndex)
}
.backgroundColor(Color.Black)
.justifyContent(FlexAlign.Center)
.height('100%')
.width("100%")
.onClick(() => {
this.controller.close()
})
}
}
2. 状态共享-父子双向
- Prop修饰符- 父组件数据更新-让子组件更新- 子组件更新-父组件不为所动
:::info
Prop是单向的,而Link修饰符则是双向的数据传递,只要使用Link修饰了传递过来的数据,这个时候就是双向同步了
注意点: 在父组件传入Link属性时,需要使用$来修饰该变量,去掉this
Next版本中可以不用$修饰,直接使用this.xx属性绑定即可
Link修饰符不允许给初始值
:::
- 将刚刚的案例改造成双向的
子组件中被@Link装饰的变量与其父组件中对应的数据源建立双向数据绑定。
@Entry
@Component
struct LinkCase {
@State
num: number = 0
build() {
Column({ space: 20 }) {
Row ({ space: 10 }) {
Text("父组件")
.fontSize(40)
Text(this.num.toString())
.fontSize(40)
}
.onClick(() => {
this.num++
})
Divider()
.width("100%")
.strokeWidth(20)
LinkChild({ num: this.num })
LinkChild({ num: $num })
// Link的修饰符传值 在Next版本这一代 进行了一代的放宽
// $num
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
}
}
@Component
struct LinkChild {
@Link
num: number
build() {
Column() {
Text("子组件" + this.num)
.onClick(() => {
this.num++
})
Swiper() {
ForEach([1,2,3,4,5,6,7,8,9,10], (item: number) => {
Row() {
Text(item.toString())
.fontColor(Color.White)
.fontSize(50)
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height(130)
.backgroundColor(Color.Blue)
})
}
.index($$this.num)
}
}
}
:::success
Link修饰符的要求- 你的父组件传值时传的必须是个响应式状态
State/Prop/
:::
:::info
需要注意的是,Link修饰的变量类型和State和Prop的类型是一样的,支持string、number、boolean、enum Object Class以及这些类型对应的数组
:::
:::success
当我们的Link,比如是一个数组的时候, 如果你循环该数组,还有一层组件,此时该组件的中的属性不能再使用Link来修饰了,因为语法不支持Link被循环之后的 传递
:::
:::info
大家一定在想,为什么不把每个菜封装成一个组件,然后用Link传递过去岂不是更方便???
我们试试
:::
:::info
看到没有,ArtTS不支持这么做,也就是Link修饰的数据必须得是最外层的 State数据,想要实现我们刚刚的设想,我们还得另辟蹊径。-后续ObjectLink 和Observerd会解决这个问题
:::
3. 状态共享-后代组件
:::info
如果我们的组件层级特别多,ArkTS支持跨组件传递状态数据来实现双向同步@Provide和 @Consume
这特别像Vue中的依赖注入
:::
- 假设我们有三层组件,Index-Child-Grand, Index的数据不想经过Child而直接给到Grand可以使用该修饰器
@Entry
@Component
struct ProvideCase02 {
@Provide count: number = 0
build() {
Row() {
Column({ space: 15 }) {
Text(this.count.toString())
.fontSize(50)
Button("顶级组件+1")
.onClick(() => {
this.count++
})
Divider()
Child()
}
.width('100%')
}
.height('100%')
}
}
@Component
struct Child {
build() {
Column() {
Text("子组件")
.fontSize(40)
Divider()
Grand()
}
}
}
@Component
struct Grand {
@Consume count: number
build() {
Column() {
Text("孙组件")
.fontSize(30)
Text(this.count.toString())
}
}
}
:::info
注意: 在不指定Provide名称的情况下,你需要使用相同的名字来定义和接收数据
:::
1)通过相同的变量名绑定
@Entry
@Component
struct Index {
@Provide
money: number = 0
build() {
Column({ space: 20 }) {
Text('父组件:' + this.money)
.onClick(() => {
this.money++
})
Parent()
}
.width('100%')
.height('100%')
.alignItems(HorizontalAlign.Center)
.justifyContent(FlexAlign.Center)
}
}
@Component
struct Parent {
@Consume
money: number
build() {
Column({ space: 20 }) {
Text('父组件:' + this.money)
.onClick(() => {
this.money++
})
Child()
}
}
}
@Component
struct Child {
@Consume
money: number
build() {
Text('子组件:' + this.money)
.onClick(() => {
this.money++
})
}
}
:::info
Object、class、string、number、boolean、enum
类型均支持- 通过相同的变量别名绑定
@Provide('key')
和@Consume('key')
key需要保持一致
:::
:::color2
ArkTS所有内容都不支持深层数据更新 UI渲染
:::
- 综合案例
:::success
声明一个登录页
:::
import { promptAction, router } from '@kit.ArkUI'
@Entry
@Component
struct ProvideBigCase {
@State
phone: string = "13812345678"
@State
code: string = "123456"
getBtnEnable () {
return !!(this.phone && this.code)
}
login () {
if(this.phone === "13812345678" && this.code === '123456') {
// 需要跳入主页
router.pushUrl({
url: 'pages/09/ProvideBigMainCase' // 一定是个Page
})
}else {
promptAction.showToast({ message: '手机号或验证码错误' })
}
}
build() {
Column({ space: 20 }) {
Image($r("app.media.login_logo"))
.width(80)
.aspectRatio(1)
Text("欢迎使用鸿蒙刷题")
.fontSize(20)
TextInput({
text: $$this.phone,
placeholder: '请输入手机号'
})
.height(50)
.width("100%")
TextInput({
text: $$this.code,
placeholder: '请输入验证码'
})
.height(50)
.width("100%")
Button("登录")
.enabled(this.getBtnEnable())
.height(50)
.width('100%')
.onClick(() => {
this.login()
})
}
.justifyContent(FlexAlign.Center)
.width('100%')
.height('100%')
.padding({
left: 40,
right: 40
})
}
}
// 登录 -》 主页 =》 拿到身份信息 =》 通过三层传递 =》 将身份信息传到每一层组件
- 跳转到主页
import { UserInfo, UserInfoModel } from './models/user'
@Entry
@Component
struct ProvideBigMainCase {
// 通过Provide 和Consume来实现 数据的传递
@Provide
user: UserInfoModel = new UserInfoModel({} as UserInfo)
// 进入页面就执行的函数
aboutToAppear(): void {
this.getCurrentUser()
}
// 获取当前的用户信息
async getCurrentUser() {
// 请求
const result = await new Promise<UserInfo>((resolve) => {
setTimeout(() => {
resolve({
id: 1,
avatar: '456',
mobile: '13812345678',
email: '123@qq.com',
nickName: '水若寒宇'
})
}, 1000)
})
this.user = new UserInfoModel(result)
}
build() {
// Tabs
Tabs() {
TabContent() {
WeChat()
}.tabBar("微信")
TabContent() {
Connect()
}.tabBar("联系人")
TabContent() {
Discover()
}.tabBar("发现")
TabContent() {
My()
}.tabBar("我的")
}.barPosition(BarPosition.End)
}
}
@Component
struct WeChat {
@Consume
user: UserInfoModel
build() {
Column() {
Text(this.user.nickName)
}
}
}
@Component
struct Connect {
@Consume
user: UserInfoModel
build() {
Column() {
Text(this.user.mobile)
}
}
}
@Component
struct My {
@Consume
user: UserInfoModel
build() {
Text(this.user.email)
}
}
@Component
struct Discover {
@Consume
user: UserInfoModel
build() {
Text(this.user.avatar)
}
}
:::success
新的知识点
router.pushUrl() . 可以实现路由的跳转-强调-地址上第一个/必须去掉,必须保证跳转的页面是Entry修饰的页面,并且在 resources/base/profile/main_pages.json中可以找到这个地址
:::