HarmonyOS MVVM模式,​viewmodel怎么和view建立关系的?

​viewmodel怎么和view建立关系的。

viewmodel数据变化的时候怎么通知view中的装饰器的?

应用示例电话簿的demo。​


HarmonyOS
2024-11-28 09:27:20
浏览
收藏 0
回答 2
待解决
回答 2
按赞同
/
按时间
因为活着就一定行

在HarmonyOS的MVVM模式中,ViewModel与View的建立关系主要是通过数据绑定来实现的。ViewModel层负责管理UI状态和交互逻辑,它监控Model数据的变化,并在数据变化时通知View更新UI。


这种模式允许View和Model之间通过ViewModel进行解耦,确保View只负责展示数据,而Model只负责数据管理。

在ArkUI框架中,ViewModel通过装饰器如​@State​​​、​​@Link​​​等来定义状态变量。这些状态变量通常作为父组件的数据源,当状态变量更新时,UI会自动刷新。例如,使用​​@State​​装饰器定义的一个状态变量,可以在View中通过绑定这个变量来动态更新UI组件,如文本、按钮等。



一个示例:电话簿应用虽然具体的电话簿Demo代码不在提供的信息中,但可以想象在一个电话簿应用中,ViewModel可能会管理一组联系人数据,并提供方法来添加、编辑和删除联系人。这些操作会通过状态变量反映出来,例如一个​​contacts​​​数组。当ViewModel中的​​contacts​​数组更新时,绑定这个数组的View组件,如列表或表格,会自动反映新的数据状态。

@Entry
@Component
struct ContactList {
    @State contacts: Contact[] = []; // 假设Contact是一个接口,定义了联系人的属性

    build() {
        ListView({
            items: this.contacts,
            itemBuilder: (contact) => {
                Text(contact.name)
                .fontSize(16)
                .onClick(() => {
                    // 编辑联系人逻辑
                });
            }
        });
    }
}

在这个示例中,​​contacts​​是一个状态变量,当其内容变化时,ListView会自动更新其展示的UI。

通过这种方式,ViewModel和View在HarmonyOS中建立了紧密的关系,使得数据驱动的UI更新成为可能,同时也保持了高层级的解耦,使得应用更加模块化和易于维护。

已于2024-11-28 11:38:58修改
分享
微博
QQ
微信
回复
2024-11-28 11:37:39
zbw_apple

​被@State修饰的变量在被更新后因为会重新触发UI渲染,也就是会重新执行build方法,所以页面会实时显示更新的数据。@Provide也具有@State的特性,同时@Provide可以与@Consume搭配实现父、子和后代组件数据同步。关于这些装饰器的内容,可在官网文档中进行详细了解:​https://developer.huawei.com/consumer/cn/doc/harmonyos-guides-V5/arkts-component-state-management-V5

应用示例电话簿的demo如下​:

//Index.ets 
import { Person, Address, AddressBook, ObservedArray} from '../viewmodel/ListViewModel' 
import emitter from '@ohos.events.emitter'; 
 
// 渲染出Person对象的名称和Observed数组<string>中的第一个号码 
// 为了更新电话号码,这里需要@ObjectLink person和@ObjectLink phones, 
// 不能使用this.person.phones,内部数组的更改不会被观察到。 
// 在AddressBookView、PersonEditView中的onClick更新selectedPerson 
@Component 
struct PersonView { 
  @ObjectLink person: Person; 
  @ObjectLink phones: ObservedArray<string>; 
  @Link selectedPerson: Person; 
 
  build() { 
    Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) { 
      Text(this.person.name) 
      if (this.phones.length) { 
        Text(this.phones[0]) 
      } 
    } 
    .height(55) 
    .backgroundColor(this.selectedPerson.name == this.person.name ? "#ffa0a0" : "#ffffff") 
    .onClick(() => { 
      this.selectedPerson = this.person; 
    }) 
  } 
} 
 
@Component 
struct phonesNumber { 
  @ObjectLink phoneNumber: ObservedArray<string> 
 
  build() { 
    Column() { 
 
      ForEach(this.phoneNumber, 
        (phone: ResourceStr, index?: number) => { 
          TextInput({ text: phone }) 
            .width(150) 
            .onChange((value) => { 
              console.log(`${index}. ${value} value has changed`) 
              this.phoneNumber[index!] = value; 
            }) 
        }, 
        (phone: ResourceStr, index: number) => `${this.phoneNumber[index] + index}` 
      ) 
    } 
  } 
} 
 
 
 
// 渲染Person的详细信息 
// @Prop装饰的变量从父组件AddressBookView深拷贝数据,将变化保留在本地, TextInput的变化只会在本地副本上进行修改。 
// 点击 "Save Changes" 会将所有数据的复制通过@Prop到@Link, 同步到其他组件 
@Component 
struct PersonEditView { 
  @Consume addrBook: AddressBook; 
  /* 指向父组件selectedPerson的引用 */ 
  @Link selectedPerson: Person; 
  /*在本地副本上编辑,直到点击保存*/ 
  @Prop name: string = ""; 
  @Prop address: Address = new Address("", 0, ""); 
  @Prop phones: ObservedArray<string> = []; 
 
  selectedPersonIndex(): number { 
    return this.addrBook.contacts.findIndex((person: Person) => person.id_ == this.selectedPerson.id_); 
  } 
 
  build() { 
    Column() { 
      TextInput({ text: this.name }) 
        .onChange((value) => { 
          this.name = value; 
        }) 
      TextInput({ text: this.address.street }) 
        .onChange((value) => { 
          this.address.street = value; 
        }) 
 
      TextInput({ text: this.address.city }) 
        .onChange((value) => { 
          this.address.city = value; 
        }) 
 
      TextInput({ text: this.address.zip.toString() }) 
        .onChange((value) => { 
          const result = Number.parseInt(value); 
          this.address.zip = Number.isNaN(result) ? 0 : result; 
        }) 
 
      if (this.phones.length > 0) { 
        phonesNumber({ phoneNumber: this.phones }) 
      } 
 
      Flex({ direction: FlexDirection.Row, justifyContent: FlexAlign.SpaceBetween }) { 
        Text("Save Changes") 
          .onClick(() => { 
            // 将本地副本更新的值赋值给指向父组件selectedPerson的引用 
            // 避免创建新对象,在现有属性上进行修改 
            this.selectedPerson.name = this.name; 
            this.selectedPerson.address = new Address(this.address.street, this.address.zip, this.address.city) 
            this.phones.forEach((phone: string, index: number) => { 
              this.selectedPerson.phones[index] = phone 
            }); 
            emitter.emit('setModelData', { 
              data: { 
                id: this.selectedPerson.id_, 
                name: this.name 
              } 
            }) 
          }) 
        Button('getData') 
          .onClick(() => { 
            emitter.emit('getModelData'); 
            this.selectedPerson = this.addrBook.me; 
          }) 
        if (this.selectedPersonIndex() != -1) { 
          Text("Delete Contact") 
            .onClick(() => { 
              let index = this.selectedPersonIndex(); 
              console.log(`delete contact at index ${index}`); 
 
              // 删除当前联系人 
              this.addrBook.contacts.splice(index, 1); 
 
              // 删除当前selectedPerson,选中态前移一位 
              index = (index < this.addrBook.contacts.length) ? index : index - 1; 
 
              // 如果contract被删除完,则设置me为选中态 
              this.selectedPerson = (index >= 0) ? this.addrBook.contacts[index] : this.addrBook.me; 
            }) 
        } 
      } 
 
    } 
  } 
} 
 
@Component 
struct AddressBookView { 
  @ObjectLink me: Person; 
  @ObjectLink contacts: ObservedArray<Person>; 
  @State selectedPerson: Person = new Person("", "", 0, "", []); 
 
  aboutToAppear() { 
    this.selectedPerson = this.me; 
  } 
 
  build() { 
    Flex({ direction: FlexDirection.Column, justifyContent: FlexAlign.Start }) { 
      Text("Me:") 
      PersonView({ 
        person: this.me, 
        phones: this.me.phones, 
        selectedPerson: this.selectedPerson 
      }) 
 
      Divider().height(8) 
 
      ForEach(this.contacts, (contact: Person) => { 
        PersonView({ 
          person: contact, 
          phones: contact.phones as ObservedArray<string>, 
          selectedPerson: this.selectedPerson 
        }) 
      }, 
        (contact: Person): string => { 
          return contact.id_; 
        } 
      ) 
 
      Divider().height(8) 
 
      Text("Edit:") 
      PersonEditView({ 
        selectedPerson: this.selectedPerson, 
        name: this.selectedPerson.name, 
        address: this.selectedPerson.address, 
        phones: this.selectedPerson.phones 
      }) 
    } 
    .borderStyle(BorderStyle.Solid).borderWidth(5).borderColor(0xAFEEEE).borderRadius(5) 
  } 
} 
 
@Entry 
@Component 
struct Index { 
  @StorageLink("contacts") @Watch("contactsChange") contacts: Array<Person> = []; 
  @Provide addrBook: AddressBook = new AddressBook( 
    new Person("Gigi", "Itamerenkatu 9", 180, "Helsinki", ["18*********", "18*********", "18*********"]), 
    [ 
      new Person("Oly", "Itamerenkatu 9", 180, "Helsinki", ["11*********", "12*********"]), 
      new Person("Sam", "Itamerenkatu 9", 180, "Helsinki", ["13*********", "14*********"]), 
      new Person("Vivi", "Itamerenkatu 9", 180, "Helsinki", ["15*********", "168*********"]), 
    ]); 
 
  contactsChange(propName: string): void { 
    this.addrBook.contacts = AppStorage.get(propName) as ObservedArray<Person>; 
  } 
 
 
  build() { 
    Column() { 
      AddressBookView({ 
        me: this.addrBook.me, 
        contacts: this.addrBook.contacts, 
        selectedPerson: this.addrBook.me 
      }) 
    } 
  } 
}
//viewmodel->ListViewModel.ets 
import emitter from '@ohos.events.emitter'; 
import { Person } from '../viewmodel/ListViewModel'; 
 
const data = [ 
  new Person("Oly1", "Itamerenkatu 9", 180, "Helsinki", ["11*********", "12*********"]), 
  new Person("Sam1", "Itamerenkatu 9", 180, "Helsinki", ["13*********", "14*********"]), 
  new Person("Vivi1", "Itamerenkatu 9", 180, "Helsinki", ["15*********", "168*********"]), 
]; 
 
export function init() { 
  emitter.on('getModelData', () => { 
    AppStorage.setOrCreate('contacts', data) 
  }) 
 
  emitter.on('setModelData', (eventData) => { 
    if (eventData.data) { 
      const changeId: string = eventData.data['id']; 
      data.forEach((person => { 
        if (person.id_ === changeId) { 
          person.name = eventData.data!['name']; 
        } 
      })) 
      AppStorage.setOrCreate('contacts', data) 
    } 
  }) 
}

viewmode和model通过状态变量进行数据更新后,viewmodel再通过状态变量的更新来触发view页面的build方法和@Builder自定义构建函数的执行;build方法和@Builder自定义构建函数的执行又会使相应的状态变量改变,如此实现“更新数据的时候直接更新视图”。

分享
微博
QQ
微信
回复
2024-11-28 14:58:32
相关问题
MVVM模式ViewModel层指的是什么?
405浏览 • 1回复 待解决
如何理解ArkUI中MVVM模式
611浏览 • 1回复 待解决
HarmonyOS hilogconsole.log关系
164浏览 • 1回复 待解决
HarmonyOS viewmodel如何与ui同步
451浏览 • 1回复 待解决
网络请求rcphttpRequest关系区别
431浏览 • 1回复 待解决
怎么理解HAR、HAP、App关系
1976浏览 • 1回复 待解决
HarmonyOS分屏幕模式小窗口模式适配
665浏览 • 1回复 待解决
鸿蒙 LiteOS 啥关系?
13615浏览 • 1回复 待解决
HarmonyOS 单纯渐变View
436浏览 • 2回复 待解决
View引用实例化一些问题
298浏览 • 1回复 待解决
怎么理解HAR、HAP、APP之间关系
1775浏览 • 1回复 待解决
如何监听Viewmodel里变量值变化
1944浏览 • 1回复 待解决