HarmonyOS鸿蒙Next中如何让从Http请求返回的JSON数据转换为@ObservedV2、@Trace装饰的类的对象具有触发UI刷新的能力

HarmonyOS鸿蒙Next中如何让从Http请求返回的JSON数据转换为@ObservedV2@Trace装饰的类的对象具有触发UI刷新的能力

@ObservedV2@Trace装饰器使用限制

  • @ObservedV2的类实例目前不支持使用JSON.stringify进行序列化。
  • 使用@ObservedV2@Trace装饰器的类,需通过new操作符实例化后,才具备被观测变化的能力。

class-transformer和reflect-metadata

可以使用class-transformer把返回的json数据转换为@ObservedV2装饰可观测的类的对象

安装

ohpm install class-transformer@0.5.1
ohpm install reflect-metadata@0.2.1

如果@ObservedV2装饰的类没有嵌套可以不需要reflect-metadata,如果有嵌套并且还要具备观测它的属性的话则需要reflect-metadata(简单讲就是如果使用了class-transformer的@Type装饰则需要import ‘reflect-metadata’)

数据源

这里采用GitHub REST API的搜索仓库的api来作为数据源:

https://api.github.com/search/repositories?q=node.js+language:javascript&sort=stars

使用@ObservedV2@Trace装饰器和class-transformer的@Type

import 'reflect-metadata'//由于用到了class-transformer的[@Type](/user/Type)装饰,所以需要引入reflect-metadata
import {Type as TypeCT} from 'class-transformer';//防止class-transformer的[@Type](/user/Type)和状态管理V2的[@Type](/user/Type)冲突,这里换个名字

[@ObservedV2](/user/ObservedV2)
class Repositories{
  @TypeCT(() => RepositoryItem)//防止class-transformer的[@Type](/user/Type)和状态管理V2的[@Type](/user/Type)冲突,这里换个名字
  [@Trace](/user/Trace)
  items: RepositoryItem[] = []

}

[@ObservedV2](/user/ObservedV2)
class RepositoryItem {
  description: string;
  full_name: string
  id: number
  name: string
  [@Trace](/user/Trace)
  stargazers_count :number

  constructor(description: string, full_name: string, id: number, name: string, stargazers_count: number) {
    this.description = description;
    this.full_name = full_name;
    this.id = id;
    this.name = name;
    this.stargazers_count = stargazers_count;
  }
}

作为栗子用,所以只取了小部分字段

axios请求http获取repositories,点击item更新star

@Entry
@ComponentV2
struct RepositoriesPage {

  @Local repositories :Repositories = new Repositories()

  aboutToAppear(): void {
    //http请求
    axios.get<Repositories,AxiosResponse<Repositories>>('/search/repositories?q=node.js+language:javascript&sort=stars',{baseURL:'https://api.github.com'})
      .then((response:AxiosResponse<Repositories>) => {
        this.repositories = response.data
      })
      .catch((e:BusinessError)=>{
        console.error('getRepositories error:'+JSON.stringify(e))
    })
  }

  build() {
    List({space:5}){
      Repeat(this.repositories.items)
        .each((riItem)=>{
          ListItem(){
            Row(){
              Text(riItem.item.name)
              Text(`star:${riItem.item.stargazers_count}`)
            }.width('100%').justifyContent(FlexAlign.SpaceBetween).padding(15)
            .backgroundColor($r('sys.color.background_secondary'))
          }.onClick(()=>{
            //修改仓库的star个数
            riItem.item.stargazers_count++
          })
        })
        .key((item) => item.id.toString())
    }.width('100%').height('100%')
    .padding(15)
  }
}

结果发现点击item之后UI并没有更新 这是由于使用@ObservedV2@Trace装饰器的类,需通过new操作符实例化后,才具备被观测变化的能力。

接下来用class-transfromer转换一下,修改一下:

//http请求
axios.get<ESObject,AxiosResponse<ESObject>>('/search/repositories?q=node.js+language:javascript&sort=stars',{baseURL:'https://api.github.com'})
  .then((response:AxiosResponse<ESObject>) => {
    this.repositories = plainToInstance(Repositories,response.data)
  })
  .catch((e:BusinessError)=>{
    console.error('getRepositories error:'+JSON.stringify(e))
})

点击item可以刷新UI了

rcp请求http数据并转换为@ObservedV2装饰的类的对象

import { rcp } from '@kit.RemoteCommunicationKit';
import { BusinessError } from '@kit.BasicServicesKit';
import {plainToInstance } from "class-transformer";

let session: rcp.Session = rcp.createSession()

async function getRepositories(search:string):Promise<Repositories>{
  let response =  await session.get(`https://api.github.com/search/repositories?q=${search}&sort=stars`)
  if(response.toJSON()){
    //通过class-transformer将普通对象转换为类实例
    return plainToInstance(Repositories, response.toJSON())
    //return response.toJSON() as Repositories //如果是这种方式且Repositories是[@ObservedV2](/user/ObservedV2)装饰的类,它将不具备被观测变化的能力
  }else {
    return Promise.reject({code:-1,message:"response is undefined"} as BusinessError<string>);
  }
}

如果是泛型类怎么办,可以让@ObservedV2@Trace装饰的对象具备被观测变化的能力吗?

修改Repositories改为泛型

[@ObservedV2](/user/ObservedV2)
class Repositories<T>{
  [@Trace](/user/Trace)
  items: T[] = []
}

页面代码:

@Entry
@ComponentV2
struct RepositoriesPage {

  @Local repositories :Repositories<RepositoryItem> = new Repositories()

  aboutToAppear(): void {
    //http请求
    axios.get<ESObject,AxiosResponse<ESObject>>('/search/repositories?q=node.js+language:javascript&sort=stars',{baseURL:'https://api.github.com'})
      .then((response:AxiosResponse<ESObject>) => {
        const targetMap:TargetMap = {
          target:Repositories,
          properties: {
            'items':RepositoryItem
          },
        }
        this.repositories = plainToInstance(Repositories,response.data,{targetMaps:[targetMap]}) as Repositories<RepositoryItem>
      })
      .catch((e:BusinessError)=>{
        console.error('getRepositories error:'+JSON.stringify(e))
    })
  }

  build() {
    List({space:5}){
      Repeat(this.repositories.items)
        .each((riItem)=>{
          ListItem(){
            Row(){
              Text(riItem.item.name)
              Text(`star:${riItem.item.stargazers_count}`)
            }.width('100%').justifyContent(FlexAlign.SpaceBetween).padding(15)
            .backgroundColor($r('sys.color.background_secondary'))
          }.onClick(()=>{
            //修改仓库的star个数
            riItem.item.stargazers_count++
          })
        })
        .key((item) => item.id.toString())
    }.width('100%').height('100%')
    .padding(15)
  }
}

搞定,点击item可以跟新UI:


更多关于HarmonyOS鸿蒙Next中如何让从Http请求返回的JSON数据转换为@ObservedV2、@Trace装饰的类的对象具有触发UI刷新的能力的实战教程也可以访问 https://www.itying.com/category-93-b0.html

3 回复

nice   学习了

更多关于HarmonyOS鸿蒙Next中如何让从Http请求返回的JSON数据转换为@ObservedV2、@Trace装饰的类的对象具有触发UI刷新的能力的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html


在HarmonyOS Next中,要让HTTP返回的JSON数据转换为@ObservedV2@Trace装饰的类对象并触发UI刷新:

  1. 使用@ObservedV2装饰数据类
  2. 在JSON解析后,确保将数据赋值给@ObservedV2类的@Trace装饰属性
  3. 属性变更会自动触发UI刷新

示例代码:

[@ObservedV2](/user/ObservedV2)
class UserData {
  [@Trace](/user/Trace) name: string = '';
}

// 解析JSON后
let user = new UserData();
user.name = jsonData.name; // 赋值会触发UI刷新,

在HarmonyOS Next中,要让HTTP请求返回的JSON数据转换为具有UI刷新能力的@ObservedV2@Trace装饰的类对象,关键点在于正确使用class-transformer进行对象转换。以下是核心要点:

  1. 必须通过new操作符实例化@ObservedV2类才能获得响应式能力,直接赋值JSON对象无效。

  2. 使用class-transformer的plainToInstance方法转换:

import { plainToInstance } from 'class-transformer';
this.repositories = plainToInstance(Repositories, response.data);
  1. 对于嵌套对象,需要使用@Type装饰器指定类型:
@TypeCT(() => RepositoryItem)
[@Trace](/user/Trace)
items: RepositoryItem[] = []
  1. 泛型类处理需要额外配置targetMaps:
const targetMap: TargetMap = {
  target: Repositories,
  properties: {
    'items': RepositoryItem
  }
}
this.repositories = plainToInstance(Repositories, response.data, {targetMaps:[targetMap]});
  1. 确保已正确安装依赖:
ohpm install class-transformer@0.5.1
ohpm install reflect-metadata@0.2.1

这种转换方式确保了从HTTP获取的数据能保持@ObservedV2@Trace的响应式特性,使UI能正确响应数据变化。

回到顶部