【关键字】
Grid组件 / 父组件 / 子组件 / @ObjectLink
【问题描述】
问题一:
点击子组件后,子组件点击事件有响应,但是父组件list没有变化,子组件的二级属性变化怎么同步到父组件的list?
问题二:
子组件中使用@ObjectLink装饰的变量属性变化时,父组件list能同步这个变化并刷新父组件UI。因为需要根据子组件变量的状态显示父组件list的内容,一旦发生变化,list就去掉这些变化的数据,父组件不显示这部分子组件。请问需要用什么其他的方案来实现?
代码示例:
【数据模型】
@Observed
export class BusinessInfo {
icon: string //图片链接
menuName?: string//菜单名称
selected?:boolean//是否选中
}
【子组件】
export default struct CourseItem {
@ObjectLink item: BusinessInfo
build() {
Column() {
Image(this.item.icon)
.width(24)
.height(24)
Text(this.item.menuName)
}
.onClick(()=>{
this.item.selected = true
})
}
}
【父组件】
@State topList: BusinessInfo[] = []
Grid() {
ForEach(this.getSelectedMenuItems(this.topList), (titleInfo: BusinessInfo, index?: number) => {
ForEach([titleInfo], (course: BusinessInfo) => {
GridItem() {
CourseItem({ item: course })
.width(33.33%)
}
}, (CourseItem: BusinessInfo) => `${CourseItem.menu_id}`)
}, (parentItem: BusinessInfo) => `${parentItem.menu_id}`)
}
//过滤选中的子组件
getSelectedMenuItems(list: BusinessInfo[] | undefined): BusinessInfo[] {
if (list == undefined) {
return []
}
let isSelected: boolean = false
let newList: BusinessInfo[] = []
for (let index = 0; index < list.length; index++) {
const element:BusinessInfo = list[index] as BusinessInfo
if (element.selected == true) {
isSelected = true
} else {
newList.push(element)
}
}
if (isSelected) {
return newList
} else {
return list
}
}
【解决方案】
问题一:
子组件中使用@ObjectLink装饰的变量可以观察到属性变化,所以子组件页面会刷新,但是主组件虽然用@state装饰了topList,但是topList中某个值变化不会被观察到。其实topList的值已经通过子组件刷新了。
如果主组件还有其他信息要根据list的内容展示,建议也封装成子组件并且通过@ObjectLink修饰。
样例如下:
let NextID: number = 1;
@Observed
class ClassA {
public id: number;
public c: number;
constructor(c: number) {
this.id = NextID++;
this.c = c;
}
}
@Component
struct ViewA {
// 子组件ViewA的@ObjectLink的类型是ClassA
@ObjectLink a: ClassA;
label: string = 'ViewA1';
build() {
Row() {
Button(`ViewA [${this.label}] this.a.c = ${this.a ? this.a.c : "undefined"}`)
.onClick(() => {
this.a.c += 1;
})
}
}
}
@Component
struct ViewC {
// 子组件ViewA的@ObjectLink的类型是ClassA
@ObjectLink a: ClassA;
label: string = 'ViewB1';
build() {
Row() {
Text(this.a.c.toString())
}
}
}
@Entry
@Component
struct ViewB {
// ViewB中有@State装饰的ClassA[]
@State arrA: ClassA[] = [new ClassA(0), new ClassA(0)];
build() {
Column() {
ForEach(this.arrA,
(item: ClassA) => {
ViewA({ label: `#${item.id}`, a: item })
},
(item: ClassA): string => item.id.toString()
)
// 使用@State装饰的数组的数组项初始化@ObjectLink,其中数组项是被@Observed装饰的ClassA的实例
ViewA({ label: `ViewA this.arrA[first]`, a: this.arrA[0] })
ViewA({ label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1] })
//封装子组件感知对象属性变化
ViewC({a:this.arrA[0]})
//@state装饰的数组内容变化不会被观察到
Text(this.arrA[0].c.toString())
}
}
}
问题二:
可以把list也封装到子组件中。