HarmonyOS鸿蒙Next中@ObservedV2和@Trace修饰渲染二维数组点击不刷新
HarmonyOS鸿蒙Next中@ObservedV2和@Trace修饰渲染二维数组点击不刷新
购物车
组件结构
@Entry
@ComponentV2
struct ShoppingCart {
@Local cartArray: Group[] = [new Group(0, false, [])];
@Local selectedItems: ChileBean[] = []; // 选中的商品数据源
@Local isAllSelected: boolean = false;
@Local allNum: number = 0;
@Local totalPrice: number = 0;
aboutToAppear() {
this.readLocalFileAndParse()
this.getSelectedChildCount()
}
readLocalFileAndParse() {
let context: common.UIAbilityContext = getContext(this) as common.UIAbilityContext;
context.resourceManager.getRawFileContent("shoppingCartThree.json", (err: BusinessError, data) => {
if (err) {
console.error('getRawFileContent err: ' + JSON.stringify(err))
return
}
let textDecoder = util.TextDecoder.create('utf-8', { ignoreBOM: true })
let decodedData = textDecoder.decodeToString(data, { stream: false })
let jsonString = decodedData.toString()
try {
let obj = JSON.parse(jsonString) as object;
let array = obj['list'] as Group[]
console.log('文件解析 ------- ' + JSON.stringify(array))
let newArray: Group[] = []
array.map((item: Group) => {
let newCart = new Group(item.id, item.check, []) // 重新赋值
newCart.name = item.name
newCart.childList = item.childList
newArray.push(newCart)
})
this.cartArray = newArray
} catch (err) {
console.error('parse err: ' + JSON.stringify(err))
}
})
}
getSelectedChildCount() {
let allNum: number = 0;
let totalPrice: number = 0;
this.cartArray.forEach((item) => {
(item.childList ?? []).forEach((child) => {
if (child.child_check) {
allNum += child.num;
totalPrice += (child.couponPrice ?? 0) * child.num;
}
if (child.child_check) {
this.selectedItems.push(child)
}
});
})
console.log('selectedItems-----------------------------' + JSON.stringify(this.selectedItems))
this.allNum = allNum;
this.totalPrice = Math.trunc(totalPrice * 100) / 100;
}
@Builder
createBottom() {
Row() {
Row() {
Text('全选')
}.padding(12)
.backgroundColor(this.isAllSelected ? Color.Orange : '#eee')
.onClick(() => {
this.isAllSelected = !this.isAllSelected;
this.cartArray.forEach(group => {
group.check = this.isAllSelected;
let childList = group.childList ?? [];
childList.forEach((item) => {
item.child_check = this.isAllSelected;
});
});
this.getSelectedChildCount()
})
Column() {
Row({ space: 12 }) {
if (this.allNum > 0) {
Text("已选" + this.allNum)
.fontSize(14)
.fontColor("#70111111")
}
Text() {
Span("合计:")
.fontSize(16)
Span("¥")
.fontSize(14)
.fontColor(Color.Orange)
.fontWeight(FontWeight.Bold)
Span(String(this.totalPrice))
.fontSize(20)
.fontColor(Color.Orange)
.fontWeight(FontWeight.Bold)
}
.margin({ right: 10 })
}
}
Text("提交")
.border({ radius: 20 })
.width(90)
.textAlign(TextAlign.Center)
.fontColor(Color.White)
.backgroundColor(this.allNum > 0 ? "#ffe5570b" : "#ffc3bdbd")
.height(45)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.onClick(() => {
if (this.allNum > 0) {
// showToast("提交数据源"+JSON.stringify(this.cartArray))
} else {
// showToast("未选择商品")
}
})
.margin({ right: 10 })
}
.width('100%')
.justifyContent(FlexAlign.SpaceBetween)
.padding({ left: 10, right: 10 })
}
build() {
Column() {
PageTitle({
label: '购物车',
type: 'Sub',
titleFontColor: 'black',
backArrowsColor: 'black',
backBtnColor: 'white',
backBtnClickCallback: () => {
router.back()
}
})
List({ space: 10 }) {
ForEach(this.cartArray, (item: Group, index: number) => {
ListItem() {
CartHeaderWidget({item:item, getSelectedChildCount:() => {
this.isAllSelected = this.cartArray.every(group => group.check); // 是否全选判断
}})
}
}, (item: Group, index: number) => JSON.stringify(item))
}.layoutWeight(1)
this.createBottom()
}.padding({ left: 10, right: 10, bottom: 10 })
.height("100%")
.width("100%")
}
}
@ComponentV2
struct CartHeaderWidget {
@Param item: Group = new Group(0, false, [])
@Event getSelectedChildCount?: () => void
build() {
Column() {
Row() {
Row() {
Text('header')
}.padding(12)
.backgroundColor(this.item.check ? Color.Orange : '#eee')
.onClick(() => {
this.item.check = !this.item.check
let childList = this.item.childList ?? [];
childList.forEach((child) => {
child.child_check = this.item.check;
})
if (this.getSelectedChildCount) {
this.getSelectedChildCount()
}
})
Text(this.item.name)
.fontSize(18)
.fontWeight(FontWeight.Bold)
.fontColor('#111')
.layoutWeight(1)
}
List() {
ForEach(this.item.childList, (child: ChileBean, child_index: number) => {
ListItem() {
CartRowWidget({
child:child,
getSelectedChildCount: () => {
this.item.check = (this.item.childList ?? []).every(everyChild => everyChild.child_check)
if (this.getSelectedChildCount) {
this.getSelectedChildCount()
}
}
})
}
},(child: ChileBean) => JSON.stringify(child))
}
}
}
}
@ComponentV2
struct CartRowWidget {
@Param @Once child: ChileBean = new ChileBean(0, false, 0)
@Event getSelectedChildCount?: () => void
build() {
Column() {
Row({ space: 12 }) {
Checkbox({ name: this.child.name ?? '' })
.selectedColor('#00BFA5')
.shape(CheckBoxShape.ROUNDED_SQUARE)
.unselectedColor('#00BFA5')
.select(this.child.child_check )
.onChange((value: boolean) => {
// 记录当前Checkbox的选中状态
this.child.child_check = value
if (this.getSelectedChildCount) {
this.getSelectedChildCount()
}
})
.width(20)
.height(20)
Image(this.child.img)
.height(90)
.width(90)
Column({ space: 10 }) {
Text(this.child.name)
.fontSize(16)
.fontWeight(FontWeight.Bold)
.fontColor('#111')
Row({ space: 12 }) {
Text() {
Span('¥')
.fontSize(14)
.fontWeight(FontWeight.Bold)
.fontColor("#ffe5570b")
Span("" + this.child.couponPrice)
.fontSize(20)
.fontWeight(FontWeight.Bold)
.fontColor("#ffe5570b")
}
ReduceWidget({child:this.child, getSelectedChildCount:this.getSelectedChildCount})
}
}
.margin({ left: 10 })
.alignItems(HorizontalAlign.Start)
}.width('100%')
.padding({ left: 10, right: 20 })
.alignItems(VerticalAlign.Center)
.width('100%')
}
}
}
@ComponentV2
struct ReduceWidget {
@Param @Once child: ChileBean = new ChileBean(0, false, 0)
@Event getSelectedChildCount?: () => void
build() {
Row({ space: 7 }) {
Text("-")
.border({ width: 1, color: Color.Gray, radius: 5 })
.width(30)
.height(20)
.textAlign(TextAlign.Center)
.onClick(() => {
if (this.child.num == 1) {
promptAction.showToast({ message: '该宝贝不能减少了!' })
} else {
this.child.num--
}
if (this.getSelectedChildCount) {
this.getSelectedChildCount()
}
})
Text(`${this.child.num}`)
.padding({ left: 3, right: 3 })
Text("+")
.border({ width: 1, color: Color.Gray, radius: 5 })
.width(30)
.height(20)
.textAlign(TextAlign.Center)
.onClick(() => {
this.child.num++
if (this.getSelectedChildCount) {
this.getSelectedChildCount()
}
})
}
}
}
[@ObservedV2](/user/ObservedV2)
export class Group {
id: number = 0
name?: string = ''
[@Trace](/user/Trace) check: boolean = false
[@Trace](/user/Trace) childList?: ChileBean[] = []
constructor(id: number, check: boolean,childList: ChileBean[] ) {
this.id = id
this.check = check
this.childList = childList
}
}
[@ObservedV2](/user/ObservedV2)
export class ChileBean {
childId: number = 0
name?: string //商品名称
img?: string //商品图
couponPrice?: number //券后价
[@Trace](/user/Trace) child_check: boolean = false //选中状态
[@Trace](/user/Trace) num: number = 0 //数量
constructor(childId: number, child_check: boolean,num:number) {
this.childId = childId
this.child_check = child_check
this.num = num
}
}
JSON 数据
{
"list": [
{
"id": 131,
"goods_default_icon": "https://isoftstone-metaservice-sit.obs.cn-north-4.myhuaweicloud.com:443/o2o%2F6917569484028618252%2FDEFAULT%2F8cbc1047f95d444cb7b3f02d7438141a.png",
"pos": "1",
"name": "婴儿护理用品专卖",
"check": false,
"childList": [
{
"childId": 1,
"child_pos": 333,
"goodsid": "508",
"num": 1,
"name": "新生儿尿布2条装",
"marketid": "0132",
"img": "https://tse3-mm.cn.bing.net/th/id/OIP-C.0OLkB4kpqaftDNKhgjOvfQHaE7?w=285&h=190&c=7&r=0&o=5&dpr=2&pid=1.7",
"price": 30.00,
"couponPrice": 28.00,
"child_check": false
},
{
"childId": 2,
"child_pos": 222,
"goodsid": "506",
"num": 1,
"name": "新生儿纯棉洗澡起泡擦",
"marketid": "0131",
"img": "https://tse4-mm.cn.bing.net/th/id/OIP-C.2fzOsOjmQAfnu6hVBLSw5gAAAA?w=231&h=180&c=7&r=0&o=5&dpr=2&pid=1.7",
"price": 30.00,
"couponPrice": 42.00,
"child_check": false
}
]
},
{
"id": 133,
"goods_default_icon": "https://isoftstone-metaservice-sit.obs.cn-north-4.myhuaweicloud.com:443/o2o%2F6917569484028618252%2FDEFAULT%2Ff0f7aa35a6d24cec8de5e7eb3088a33e.png",
"pos": "221",
"name": "新风手机专卖店",
"check": false,
"childList": [
{
"childId": 3,
"child_pos": 415,
"goodsid": "508",
"num": 1,
"name": "新生儿尿布2条装",
"marketid": "0131",
"img": "https://tse3-mm.cn.bing.net/th/id/OIP-C.0OLkB4kpqaftDNKhgjOvfQHaE7?w=285&h=190&c=7&r=0&o=5&dpr=2&pid=1.7",
"price": 48.00,
"couponPrice": 39.00,
"child_check": false
},
{
"childId": 4,
"child_pos": 414,
"goodsid": "821",
"num": 1,
"name": "新生儿纯棉洗澡起泡擦",
"marketid": "0132",
"img": "https://tse4-mm.cn.bing.net/th/id/OIP-C.2fzOsOjmQAfnu6hVBLSw5gAAAA?w=231&h=180&c=7&r=0&o=5&dpr=2&pid=1.7",
"price": 98.00,
"couponPrice": 62.00,
"child_check": false
}
]
}
]
}
更多关于HarmonyOS鸿蒙Next中@ObservedV2和@Trace修饰渲染二维数组点击不刷新的实战教程也可以访问 https://www.itying.com/category-93-b0.html
更多关于HarmonyOS鸿蒙Next中@ObservedV2和@Trace修饰渲染二维数组点击不刷新的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,@ObservedV2
和@Trace
修饰二维数组点击不刷新时,需检查以下几点:
- 确保二维数组每个元素都使用
@ObservedV2
修饰 @Trace
需正确绑定到触发更新的方法上- 数组变更需通过set方法触发响应式更新
- 检查是否在自定义组件中正确使用了
@Component
装饰器
若仍不刷新,可尝试将二维数组改为对象数组,或使用@Track
单独标记需要跟踪的属性。数据变更必须通过赋值新数组或对象的方式触发更新。
在HarmonyOS Next中,@ObservedV2和@Trace修饰的二维数组点击不刷新的问题,通常是由于数据变更未正确触发UI更新。从代码来看,需要注意以下几点:
-
确保所有需要响应式更新的属性都正确使用了@Trace修饰。在您的代码中,Group和ChileBean类的check、child_check、num等属性已正确添加@Trace。
-
对于嵌套数组(childList),虽然数组本身被@Trace修饰,但直接修改数组元素可能不会触发更新。建议在修改数组元素后,重新赋值整个数组:
// 修改childList中的元素后 this.item.childList = [...this.item.childList];
-
检查CartHeaderWidget组件中的参数传递方式。当前使用@Param传递item对象,确保在父组件中修改item属性后,子组件能接收到更新。
-
在ReduceWidget组件中,虽然child参数使用了@Once修饰,但实际需要响应式更新,建议移除@Once修饰符。
-
确保所有状态变更后都调用了getSelectedChildCount回调来触发重新计算。
这些修改应该能解决二维数组点击不刷新的问题。