HarmonyOS NEXT应用开发之@Observed装饰器和 @ObjectLink装饰器:嵌套类对象属性变化(1)

本文详细介绍了在HarmonyOS应用开发中,@Observed和@ObjectLink装饰器的使用,特别是它们如何处理嵌套类对象属性的变化。通过多个示例,展示了如何在不同场景下使用这两个装饰器,包括数组、Map和Set等数据结构,以及如何在组件间传递和响应数据变化。文章还提到了华为HarmonyOS开发的学习资源和进阶课程,旨在帮助开发者提升技能。
摘要由CSDN通过智能技术生成
        this.c.c += 1;
        console.log('this.c.c:' + this.c.c)
      })
  }
  .width(300)
}

}
}

@Entry
@Component
struct ViewB {
@State b: ClassB = new ClassB(new ClassA(0));
@State child: ClassD = new ClassD(new ClassC(0));

build() {
Column() {
ViewC({ label: ‘ViewC #3’,
c: this.child.c })
Button(ViewC: this.child.c.c add 10)
.backgroundColor(‘#ff7fcf58’)
.onClick(() => {
this.child.c.c += 10
console.log(‘this.child.c.c:’ + this.child.c.c)
})
}
}
}


被@Observed装饰的ClassC类,可以观测到继承基类的属性的变化。


ViewB中的事件句柄:


* this.child.c = new ClassA(0) 和this.b = new ClassB(new ClassA(0)): 对@State装饰的变量b和其属性的修改。
* this.child.c.c = … :该变化属于第二层的变化,@State无法观察到第二层的变化,但是ClassA被@Observed装饰,ClassA的属性c的变化可以被@ObjectLink观察到。


ViewC中的事件句柄:


* this.c.c += 1:对@ObjectLink变量a的修改,将触发Button组件的刷新。@ObjectLink和@Prop不同,@ObjectLink不拷贝来自父组件的数据源,而是在本地构建了指向其数据源的引用。
* @ObjectLink变量是只读的,this.a = new ClassA(…)是不允许的,因为一旦赋值操作发生,指向数据源的引用将被重置,同步将被打断。


#### 对象数组


对象数组是一种常用的数据结构。以下示例展示了数组对象的用法。



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;
})
}
}
}

@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] })

  Button(`ViewB: reset array`)
    .onClick(() => {
      this.arrA = [new ClassA(0), new ClassA(0)];
    })
  Button(`ViewB: push`)
    .onClick(() => {
      this.arrA.push(new ClassA(0))
    })
  Button(`ViewB: shift`)
    .onClick(() => {
      if (this.arrA.length > 0) {
        this.arrA.shift()
      } else {
        console.log("length <= 0")
      }
    })
  Button(`ViewB: chg item property in middle`)
    .onClick(() => {
      this.arrA[Math.floor(this.arrA.length / 2)].c = 10;
    })
  Button(`ViewB: chg item property in middle`)
    .onClick(() => {
      this.arrA[Math.floor(this.arrA.length / 2)] = new ClassA(11);
    })
}

}
}


* this.arrA[Math.floor(this.arrA.length/2)] = new ClassA(…) :该状态变量的改变触发2次更新:


	1. ForEach:数组项的赋值导致ForEach的[itemGenerator](arkts-rendering-control-foreach.md#%E6%8E%A5%E5%8F%A3%E6%8F%8F%E8%BF%B0)被修改,因此数组项被识别为有更改,ForEach的item builder将执行,创建新的ViewA组件实例。
	2. ViewA({ label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1] }):上述更改改变了数组中第二个元素,所以绑定this.arrA[1]的ViewA将被更新。
* this.arrA.push(new ClassA(0)) : 将触发2次不同效果的更新:


	1. ForEach:新添加的ClassA对象对于ForEach是未知的 itemGenerator ,ForEach的item builder将执行,创建新的ViewA组件实例。
	2. ViewA({ label: `ViewA this.arrA[last]`, a: this.arrA[this.arrA.length-1] }):数组的最后一项有更改,因此引起第二个ViewA的实例的更改。对于ViewA({ label: `ViewA this.arrA[first]`, a: this.arrA[0] }),数组的更改并没有触发一个数组项更改的改变,所以第一个ViewA不会刷新。
* this.arrA[Math.floor(this.arrA.length/2)].c:@State无法观察到第二层的变化,但是ClassA被@Observed装饰,ClassA的属性的变化将被@ObjectLink观察到。


#### 二维数组


使用@Observed观察二维数组的变化。可以声明一个被@Observed装饰的继承Array的子类。



@Observed
class StringArray extends Array {
}


使用new StringArray()来构造StringArray的实例,new运算符使得@Observed生效,@Observed观察到StringArray的属性变化。


声明一个从Array扩展的类class StringArray extends Array<String> {},并创建StringArray的实例。@Observed装饰的类需要使用new运算符来构建class实例。



@Observed
class StringArray extends Array {
}

@Component
struct ItemPage {
@ObjectLink itemArr: StringArray;

build() {
Row() {
Text(‘ItemPage’)
.width(100).height(100)

  ForEach(this.itemArr,
    (item: string | Resource) => {
      Text(item)
        .width(100).height(100)
    },
    (item: string) => item
  )
}

}
}

@Entry
@Component
struct IndexPage {
@State arr: Array = [new StringArray(), new StringArray(), new StringArray()];

build() {
Column() {
ItemPage({ itemArr: this.arr[0] })
ItemPage({ itemArr: this.arr[1] })
ItemPage({ itemArr: this.arr[2] })
Divider()

  ForEach(this.arr,
    (itemArr: StringArray) => {
      ItemPage({ itemArr: itemArr })
    },
    (itemArr: string) => itemArr[0]
  )

  Divider()

  Button('update')
    .onClick(() => {
      console.error('Update all items in arr');
      if ((this.arr[0] as Array<String>)[0] !== undefined) {
        // 正常情况下需要有一个真实的ID来与ForEach一起使用,但此处没有
        // 因此需要确保推送的字符串是唯一的。
        this.arr[0].push(`${this.arr[0].slice(-1).pop()}${this.arr[0].slice(-1).pop()}`);
        this.arr[1].push(`${this.arr[1].slice(-1).pop()}${this.arr[1].slice(-1).pop()}`);
        this.arr[2].push(`${this.arr[2].slice(-1).pop()}${this.arr[2].slice(-1).pop()}`);
      } else {
        this.arr[0].push('Hello');
        this.arr[1].push('World');
        this.arr[2].push('!');
      }
    })
}

}
}


#### 继承Map类



> 
> **说明:**
> 
> 
> 从API version 11开始,@ObjectLink支持@Observed装饰Map类型和继承Map类的类型。
> 
> 
> 


在下面的示例中,myMap类型为MyMap<number, string>,点击Button改变myMap的属性,视图会随之刷新。



@Observed
class ClassA {
public a: MyMap<number, string>;

constructor(a: MyMap<number, string>) {
this.a = a;
}
}

@Observed
export class MyMap<K, V> extends Map<K, V> {
public name: string;

constructor(name?: string, args?: [K, V][]) {
super(args);
this.name = name ? name : “My Map”;

  • 12
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值