当前位置: 首页 > news >正文

【鸿蒙学习笔记】MVVM模式

官方文档:MVVM模式

[Q&A] 什么是MVVM

ArkUI采取MVVM = Model + View + ViewModel模式。

  1. Model层:存储数据和相关逻辑的模型。
  2. View层:在ArkUI中通常是@Component装饰组件渲染的UI。
  3. ViewModel层:在ArkUI中,ViewModel是存储在自定义组件的状态变量LocalStorageAppStorage中的数据
    在这里插入图片描述

MVVM应用示例

开发一个电话簿应用,实现功能如下:

  1. 显示联系人和设备(“Me”)电话号码 。
  2. 选中联系人时,进入可编辑态“Edit”,可以更新该联系人详细信息,包括电话号码,住址。
  3. 在更新联系人信息时,只有在单击保存“Save Changes”之后,才会保存更改。
  4. 可以点击删除联系人“Delete Contact”,可以在联系人列表删除该联系人。

Model

Address,Person,AddressBook,MyArray

ViewModel

PersonView,phonesNumber,PersonEditView,AddressBookView

Vidw

PracExample

// ViewModel classes
let nextId = 0;@Observed
export class MyArray<T> extends Array<T> {constructor(args: T[]) {console.log(`MyArray: ${JSON.stringify(args)} `)if (args instanceof Array) {super(...args);} else {super(args)}}
}@Observed
export class Address {street: string;zip: number;city: string;constructor(street: string,zip: number,city: string) {this.street = street;this.zip = zip;this.city = city;}
}@Observed
export class Person {id_: string;name: string;address: Address;phones: MyArray<string>;constructor(name: string,street: string,zip: number,city: string,phones: string[]) {this.id_ = `${nextId}`;nextId++;this.name = name;this.address = new Address(street, zip, city);this.phones = new MyArray<string>(phones);}
}export class AddressBook {me: Person;friends: MyArray<Person>;constructor(me: Person, contacts: Person[]) {this.me = me;this.friends = new MyArray<Person>(contacts);}
}// 渲染出Person对象的名称和Observed数组<string>中的第一个号码
// 为了更新电话号码,这里需要@ObjectLink person和@ObjectLink phones,
// 不能使用this.person.phones,内部数组的更改不会被观察到。
// 在AddressBookView、PersonEditView中的onClick更新selectedPerson
@Component
struct PersonView {@ObjectLink person: Person;@ObjectLink phones: MyArray<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 ? Color.Pink : "#ffffff").onClick(() => {this.selectedPerson = this.person;})}
}@Component
struct phonesNumber {@ObjectLink phoneNumber: MyArray<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: MyArray<string> = [];selectedPersonIndex(): number {return this.addrBook.friends.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});})if (this.selectedPersonIndex() != -1) {Text("Delete Contact").onClick(() => {let index = this.selectedPersonIndex();console.log(`delete contact at index ${index}`);// 删除当前联系人this.addrBook.friends.splice(index, 1);// 删除当前selectedPerson,选中态前移一位index = (index < this.addrBook.friends.length) ? index : index - 1;// 如果contract被删除完,则设置me为选中态this.selectedPerson = (index >= 0) ? this.addrBook.friends[index] : this.addrBook.me;})}}}}
}@Component
struct AddressBookView {@ObjectLink me: Person;@ObjectLink contacts: MyArray<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 MyArray<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 PracExample {@Provide addrBook: AddressBook = new AddressBook(new Person("卧龙", "南路 9", 180, "大连", ["18*********", "18*********", "18*********"]),[new Person("小华", "东路 9", 180, "大连", ["11*********", "12*********"]),new Person("小刚", "西边路 9", 180, "大连", ["13*********", "14*********"]),new Person("小红", "南方街 9", 180, "大连", ["15*********", "168*********"]),]);build() {Column() {AddressBookView({me: this.addrBook.me,contacts: this.addrBook.friends,selectedPerson: this.addrBook.me})}}
}

在这里插入图片描述

相关文章:

【鸿蒙学习笔记】MVVM模式

官方文档&#xff1a;MVVM模式 [Q&A] 什么是MVVM ArkUI采取MVVM Model View ViewModel模式。 Model层&#xff1a;存储数据和相关逻辑的模型。View层&#xff1a;在ArkUI中通常是Component装饰组件渲染的UI。ViewModel层&#xff1a;在ArkUI中&#xff0c;ViewModel是…...

端、边、云三级算力网络

目录 端、边、云三级算力网络 NPU Arm架构 OpenStack kubernetes k3s轻量级Kubernetes kubernetes和docker区别 DCI(Data Center Interconnect) SD/WAN TF 端、边、云三级算力网络 算力网络从传统云网融合的角度出发,结合 边缘计算、网络云化以及智能控制的优势,通…...

java —— JSP 技术

一、JSP &#xff08;一&#xff09;前言 1、.jsp 与 .html 一样属于前端内容&#xff0c;创建在 WebContent 之下&#xff1b; 2、嵌套的 java 语句放置在<% %>里面&#xff1b; 3、嵌套 java 语句的三种语法&#xff1a; ① 脚本&#xff1a;<% java 代码 %>…...

【Python学习笔记】菜鸟教程Scrapy案例 + B站amazon案例视频

背景前摇&#xff08;省流可以跳过这部分&#xff09; 实习的时候厚脸皮请教了一位办公室负责做爬虫这块的老师&#xff0c;给我推荐了Scrapy框架。 我之前学过一些爬虫基础&#xff0c;但是用的是比较常见的BeautifulSoup和Request&#xff0c;于是得到Scrapy这个关键词后&am…...

Pycharm的终端(Terminal)中切换到当前项目所在的虚拟环境

1.在Pycharm最下端点击终端/Terminal, 2.点击终端窗口最上端最右边的∨&#xff0c; 3.点击Command Prompt&#xff0c;切换环境&#xff0c; 可以看到现在环境已经由默认的PS(Window PowerShell)切换为项目所使用的虚拟环境。 4.更近一步&#xff0c;如果想让Pycharm默认显示…...

Nginx 高效加速策略:动静分离与缓存详解

在现代Web开发中&#xff0c;网站性能是衡量用户体验的关键指标之一。Nginx&#xff0c;以其出色的性能和灵活性&#xff0c;成为众多网站架构中不可或缺的一部分。本文将深度解析如何利用Nginx实现动静分离与缓存&#xff0c;从而大幅提升网站加载速度和响应效率。 理解动静分…...

Unity3D 游戏摇杆的制作与实现详解

在Unity3D游戏开发中&#xff0c;摇杆是一种非常常见的输入方式&#xff0c;特别适用于移动设备的游戏控制。本文将详细介绍如何在Unity3D中制作和实现一个虚拟摇杆&#xff0c;包括技术详解和代码实现。 对惹&#xff0c;这里有一个游戏开发交流小组&#xff0c;大家可以点击…...

从nginx返回404来看http1.0和http1.1的区别

序言 什么样的人可以称之为有智慧的人呢&#xff1f;如果下一个定义&#xff0c;你会如何来定义&#xff1f; 所谓智慧&#xff0c;就是能区分自己能改变的部分&#xff0c;自己无法改变的部分&#xff0c;努力去做自己能改变的&#xff0c;而不要天天想着那些无法改变的东西&a…...

MySQL 代理层:ProxySQL

文章目录 说明安装部署1.1 yum 安装1.2 启停管理1.3 查询版本1.4 Admin 管理接口 入门体验功能介绍3.1 多层次配置系统 读写分离将实例接入到代理服务定义主机组之间的复制关系配置路由规则事务读的配置延迟阈值和请求转发 ProxySQL 核心表mysql_usersmysql_serversmysql_repli…...

异步主从复制

主从复制的概念 主从复制是一种在数据库系统中常用的数据备份和读取扩展技术&#xff0c;通过将一个数据库服务器&#xff08;主服务器&#xff09;上的数据变更自动同步到一个或多个数据库服务器&#xff08;从服务器&#xff09;上&#xff0c;以此来实现数据的冗余备份、读…...

论文解析——Full Stack Optimization of Transformer Inference: a Survey

作者及发刊详情 摘要 正文 主要工作贡献 这篇文章的贡献主要有两部分&#xff1a; 分析Transformer的特征&#xff0c;调查高效transformer推理的方法通过应用方法学展现一个DNN加速器生成器Gemmini的case研究 1&#xff09;分析和解析Transformer架构的运行时特性和瓶颈…...

selenium处理cookie问题实战

1. cookie获取不完整 需要进入的资损平台(web)首页&#xff0c;才会出现有效的ctoken等信息 1.1. 原因说明 未进入指定页面而获取的 cookie 与进入页面后获取的 cookie 可能会有一些差异&#xff0c;这取决于网站的具体实现和 cookie 的设置方式。 通常情况下&#xff0c;一些…...

(十五)GLM库对矩阵操作

GLM简单使用 glm是一个开源的对矩阵运算的库&#xff0c;下载地址&#xff1a; https://github.com/g-truc/glm/releases 直接包含其头文件即可使用&#xff1a; #include <glad/glad.h>//glad必须在glfw头文件之前包含 #include <GLFW/glfw3.h> #include <io…...

android中activity与fragment之间的各种跳转

我们以音乐播放、视频播放、用户注册与登录为例【Musicfragment&#xff08;音乐列表页&#xff09;、Videofragment&#xff08;视频列表页&#xff09;、MusicAvtivity&#xff08;音乐详情页&#xff09;、VideoFragment&#xff08;视频详情页&#xff09;、LoginActivity&…...

动态规划算法-以中学排课管理系统为例

1.动态规划算法介绍 1.算法思路 动态规划算法通常用于求解具有某种最优性质的问题。在这类问题中&#xff0c;可能会有许多可行解。每一个解都对应于一个值&#xff0c;我们希望找到具有最优值的解。动态规划算法与分治法类似&#xff0c;其基本思想也是将待求解问题分解成若…...

本安防爆手机:危险环境下的安全通信解决方案

在石油化工、煤矿、天然气等危险环境中&#xff0c;通信安全是保障工作人员生命安全和生产顺利进行的关键。防爆智能手机作为专为这些环境设计的通信工具&#xff0c;提供了全方位的安全通信解决方案。 防爆设计与材料&#xff1a; 防爆智能手机采用特殊的防爆结构和材料&…...

算法学习笔记(8)-动态规划基础篇

目录 基础内容&#xff1a; 动态规划&#xff1a; 动态规划理解的问题引入&#xff1a; 解析&#xff1a;&#xff08;暴力回溯&#xff09; 代码示例&#xff1a; 暴力搜索&#xff1a; Dfs代码示例&#xff1a;&#xff08;搜索&#xff09; 暴力递归产生的递归树&…...

数据库常见问题(持续更新)

数据库常见问题(持续更新) 1、数据库范式&#xff1f; 1NF&#xff1a;不可分割2NF&#xff1a;没有非主属性对候选码存在部分依赖3NF&#xff1a;没有非主属性传递依赖候选码BCNF&#xff1a;消除了主属性对对候选码的传递依赖或部分依赖 2、InnoDB事务的实现&#xff1f; …...

定个小目标之刷LeetCode热题(40)

94. 二叉树的中序遍历 给定一个二叉树的根节点 root &#xff0c;返回 它的 中序 遍历 。 直接上代码吧&#xff0c;中序遍历左根右 class Solution {public List<Integer> inorderTraversal(TreeNode root) {List<Integer> res new ArrayList<Integer>(…...

Linux--线程(概念篇)

目录 1.背景知识 再谈地址空间&#xff1a; 关于页表&#xff08;32bit机器上&#xff09; 2.线程的概念和Linux中线程的实现 概念部分&#xff1a; 代码部分&#xff1a; 问题&#xff1a; 3.关于线程的有点与缺点 4.进程VS线程 1.背景知识 再谈地址空间&#xff1a…...

本地柴油发电机组排行2023年最新榜单

柴油发电机是通过燃烧柴油驱动发动机&#xff0c;进而发电的设备&#xff0c;广泛应用于电力中断或无电网地区。1. 柴油发电机的核心工作原理是什么&#xff1f;柴油发电机是一种将化学能转化为电能的设备&#xff0c;其核心是柴油发动机与交流发电机的组合。当柴油在发动机内燃…...

告别拍脑袋规划!用ArcGIS做绿道选线:如何科学量化坡度、水域、道路成本并加权计算

科学规划绿道的ArcGIS高阶技法&#xff1a;从成本栅格构建到最优路径生成绿道规划从来不是简单的"两点之间直线最短"&#xff0c;而是需要综合考虑地形、生态、人文等多维因素的复杂决策过程。传统规划中常见的"拍脑袋"决策方式&#xff0c;往往导致建成后…...

环境光遮蔽(Ambient Occlusion):揭秘那个让虚拟世界“有重量感“的阴影魔法

一、一个让我"开窍"的老木匠故事 我有个朋友是传统家具的修复师&#xff0c;他给我讲过一个让我至今难忘的故事。他说他刚入行时跟着一位 70 多岁的老木匠师父学习——师父让他做的第一件事不是雕花、不是榫卯——而是"看阴影"——这个看似奇怪的训练改变了…...

为Alchitry Au FPGA开发板外接JTAG接口的完整指南

1. 项目概述与核心价值如果你正在使用基于Xilinx Artix-7 FPGA的Alchitry Au或Au开发板&#xff0c;并且已经厌倦了每次调试或烧录都要依赖板载的USB-JTAG桥接芯片&#xff0c;或者你的项目已经将板载USB接口挪作他用&#xff0c;那么为你的开发板外接一个独立的JTAG调试器&…...

Adobe-GenP 3.0:轻松激活Adobe全家桶的完整指南

Adobe-GenP 3.0&#xff1a;轻松激活Adobe全家桶的完整指南 【免费下载链接】Adobe-GenP Adobe CC 2019/2020/2021/2022/2023 GenP Universal Patch 3.0 项目地址: https://gitcode.com/gh_mirrors/ad/Adobe-GenP Adobe-GenP 3.0是一款专为Adobe Creative Cloud系列软件…...

从模糊到电影级景深:Midjourney + Topaz Gigapixel联调方案(含LUT预设包+PSD分层模板)

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;从模糊到电影级景深&#xff1a;Midjourney Topaz Gigapixel联调方案&#xff08;含LUT预设包PSD分层模板&#xff09; 当Midjourney生成的图像存在主体边缘柔化、背景层次缺失或分辨率不足等问题时&#xf…...

Arduino ADC自检:用RC电路诊断模数转换器故障

1. 项目概述&#xff1a;当你的体重秤开始“说谎”你有没有遇到过这样的情况&#xff1a;站上家里的电子体重秤&#xff0c;屏幕上跳出来的数字让你瞬间怀疑人生&#xff1f;要么是轻得离谱&#xff0c;要么是重得吓人&#xff0c;更诡异的是&#xff0c;它可能只在两个固定的、…...

Lovable电商网站搭建:如何用不到3人技术团队,72小时内上线PCI-DSS合规MVP版本?

更多请点击&#xff1a; https://codechina.net 第一章&#xff1a;Lovable电商网站搭建 Lovable 是一个面向中小商户的轻量级电商解决方案&#xff0c;采用现代 Web 技术栈构建&#xff0c;强调可扩展性、用户体验与快速部署能力。本章将指导你从零开始搭建一个具备商品展示、…...

从API Key管理视角看Taotoken平台的安全与审计功能

&#x1f680; 告别海外账号与网络限制&#xff01;稳定直连全球优质大模型&#xff0c;限时半价接入中。 &#x1f449; 点击领取海量免费额度 从API Key管理视角看Taotoken平台的安全与审计功能 对于依赖大模型API进行开发的团队而言&#xff0c;API Key的管理与安全是项目稳…...

Jetson Orin上TVA模型DLA精准卸载配置

重磅预告&#xff1a;本专栏将独家连载系列丛书《智能体视觉技术与应用》部分精华内容&#xff0c;该书是世界首套系统阐述“因式智能体”视觉理论与实践的专著&#xff0c;特邀美国 TypeOne 公司首席科学家、斯坦福大学博士 Bohan 担任技术顾问。Bohan先生师从美国三院院士、“…...